Merge branch 'dev' of https://github.com/Regalis11/Barotrauma.git into unstable-tests
This commit is contained in:
@@ -50,39 +50,39 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public int DockingDir { get; set; }
|
||||
|
||||
[Serialize("32.0,32.0", false, description: "How close the docking port has to be to another port to dock.")]
|
||||
[Serialize("32.0,32.0", IsPropertySaveable.No, description: "How close the docking port has to be to another port to dock.")]
|
||||
public Vector2 DistanceTolerance { get; set; }
|
||||
|
||||
[Serialize(32.0f, false, description: "How close together the docking ports are forced when docked.")]
|
||||
[Serialize(32.0f, IsPropertySaveable.No, description: "How close together the docking ports are forced when docked.")]
|
||||
public float DockedDistance
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(true, false, description: "Is the port horizontal.")]
|
||||
[Serialize(true, IsPropertySaveable.No, description: "Is the port horizontal.")]
|
||||
public bool IsHorizontal
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, true, description: "If set to true, this docking port is used when spawning the submarine docked to an outpost (if possible).")]
|
||||
[Editable, Serialize(false, IsPropertySaveable.Yes, description: "If set to true, this docking port is used when spawning the submarine docked to an outpost (if possible).")]
|
||||
public bool MainDockingPort
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(true, false, description: "Should the OnUse StatusEffects trigger when docking (on vanilla docking ports these effects emit particles and play a sound).)")]
|
||||
[Editable, Serialize(true, IsPropertySaveable.No, description: "Should the OnUse StatusEffects trigger when docking (on vanilla docking ports these effects emit particles and play a sound).)")]
|
||||
public bool ApplyEffectsOnDocking
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(DirectionType.None, false, description: "Which direction the port is allowed to dock in. For example, \"Top\" would mean the port can dock to another port above it.\n"+
|
||||
"Normally there's no need to touch this setting, but if you notice the docking position is incorrect (for example due to some unusual docking port configuration without hulls or doors), you can use this to enforce the direction.")]
|
||||
[Editable, Serialize(DirectionType.None, IsPropertySaveable.No, description: "Which direction the port is allowed to dock in. For example, \"Top\" would mean the port can dock to another port above it.\n"+
|
||||
"Normally there's no need to touch this setting, but if you notice the docking position is incorrect (for example due to some unusual docking port configuration without hulls or doors), you can use this to enforce the direction.")]
|
||||
public DirectionType ForceDockingDirection { get; set; }
|
||||
|
||||
public DockingPort DockingTarget { get; private set; }
|
||||
@@ -126,11 +126,11 @@ namespace Barotrauma.Items.Components
|
||||
/// </summary>
|
||||
public event Action OnUnDocked;
|
||||
|
||||
public DockingPort(Item item, XElement element)
|
||||
public DockingPort(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
// isOpen = false;
|
||||
foreach (XElement subElement in element.Elements())
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
string texturePath = subElement.GetAttributeString("texture", "");
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
@@ -338,33 +338,32 @@ namespace Barotrauma.Items.Components
|
||||
Vector2.UnitX * DockingDir :
|
||||
Vector2.UnitY * DockingDir;
|
||||
offset *= DockedDistance * 0.5f * item.Scale;
|
||||
|
||||
Vector2 pos1 = item.WorldPosition + offset;
|
||||
|
||||
Vector2 pos1 = item.WorldPosition + offset;
|
||||
Vector2 pos2 = DockingTarget.item.WorldPosition - offset;
|
||||
|
||||
if (useWeldJoint)
|
||||
{
|
||||
joint = JointFactory.CreateWeldJoint(GameMain.World,
|
||||
item.Submarine.PhysicsBody.FarseerBody, DockingTarget.item.Submarine.PhysicsBody.FarseerBody,
|
||||
ConvertUnits.ToSimUnits(pos1), FarseerPhysics.ConvertUnits.ToSimUnits(pos2), true);
|
||||
ConvertUnits.ToSimUnits(pos1), ConvertUnits.ToSimUnits(pos2), true);
|
||||
|
||||
((WeldJoint)joint).FrequencyHz = 1.0f;
|
||||
joint.CollideConnected = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
var distanceJoint = JointFactory.CreateDistanceJoint(GameMain.World,
|
||||
item.Submarine.PhysicsBody.FarseerBody, DockingTarget.item.Submarine.PhysicsBody.FarseerBody,
|
||||
ConvertUnits.ToSimUnits(pos1), FarseerPhysics.ConvertUnits.ToSimUnits(pos2), true);
|
||||
ConvertUnits.ToSimUnits(pos1), ConvertUnits.ToSimUnits(pos2), true);
|
||||
|
||||
distanceJoint.Length = 0.01f;
|
||||
distanceJoint.Frequency = 1.0f;
|
||||
distanceJoint.DampingRatio = 0.8f;
|
||||
|
||||
joint = distanceJoint;
|
||||
joint.CollideConnected = true;
|
||||
}
|
||||
|
||||
joint.CollideConnected = true;
|
||||
}
|
||||
|
||||
public int GetDir(DockingPort dockingTarget = null)
|
||||
@@ -476,6 +475,10 @@ namespace Barotrauma.Items.Components
|
||||
wire.Connect(powerConnection, false, false);
|
||||
recipient.TryAddLink(wire);
|
||||
wire.Connect(recipient, false, false);
|
||||
|
||||
//Flag connections to be updated
|
||||
Powered.ChangedConnections.Add(powerConnection);
|
||||
Powered.ChangedConnections.Add(recipient);
|
||||
}
|
||||
|
||||
private void CreateDoorBody()
|
||||
@@ -545,7 +548,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
//expand hulls if needed, so there's no empty space between the sub's hulls and docking port hulls
|
||||
int leftSubRightSide = int.MinValue, rightSubLeftSide = int.MaxValue;
|
||||
foreach (Hull hull in Hull.hullList)
|
||||
foreach (Hull hull in Hull.HullList)
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
@@ -649,7 +652,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
//expand hulls if needed, so there's no empty space between the sub's hulls and docking port hulls
|
||||
int upperSubBottom = int.MaxValue, lowerSubTop = int.MinValue;
|
||||
foreach (Hull hull in Hull.hullList)
|
||||
foreach (Hull hull in Hull.HullList)
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
@@ -757,7 +760,9 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
LinkHullsToGaps();
|
||||
|
||||
|
||||
Item.UpdateHulls();
|
||||
|
||||
hulls[0].ShouldBeSaved = false;
|
||||
hulls[1].ShouldBeSaved = false;
|
||||
item.linkedTo.Add(hulls[0]);
|
||||
@@ -907,6 +912,13 @@ namespace Barotrauma.Items.Components
|
||||
DockingTarget.Undock();
|
||||
DockingTarget = null;
|
||||
|
||||
//Flag power connection
|
||||
Connection powerConnection = Item.Connections.Find(c => c.IsPower);
|
||||
if (powerConnection != null)
|
||||
{
|
||||
Powered.ChangedConnections.Add(powerConnection);
|
||||
}
|
||||
|
||||
if (doorBody != null)
|
||||
{
|
||||
GameMain.World.Remove(doorBody);
|
||||
@@ -1048,8 +1060,8 @@ namespace Barotrauma.Items.Components
|
||||
if (initialized) { return; }
|
||||
initialized = true;
|
||||
|
||||
float maxXDist = (item.Prefab.sprite.size.X * item.Prefab.Scale) / 2;
|
||||
float closestYDist = (item.Prefab.sprite.size.Y * item.Prefab.Scale) / 2;
|
||||
float maxXDist = (item.Prefab.Sprite.size.X * item.Prefab.Scale) / 2;
|
||||
float closestYDist = (item.Prefab.Sprite.size.Y * item.Prefab.Scale) / 2;
|
||||
foreach (Item it in Item.ItemList)
|
||||
{
|
||||
if (it.Submarine != item.Submarine) { continue; }
|
||||
|
||||
@@ -100,7 +100,7 @@ namespace Barotrauma.Items.Components
|
||||
public bool CanBeWelded = true;
|
||||
|
||||
private float stuck;
|
||||
[Serialize(0.0f, false, description: "How badly stuck the door is (in percentages). If the percentage reaches 100, the door needs to be cut open to make it usable again.")]
|
||||
[Serialize(0.0f, IsPropertySaveable.No, description: "How badly stuck the door is (in percentages). If the percentage reaches 100, the door needs to be cut open to make it usable again.")]
|
||||
public float Stuck
|
||||
{
|
||||
get { return stuck; }
|
||||
@@ -115,13 +115,13 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(3.0f, true, description: "How quickly the door opens."), Editable]
|
||||
[Serialize(3.0f, IsPropertySaveable.Yes, description: "How quickly the door opens."), Editable]
|
||||
public float OpeningSpeed { get; private set; }
|
||||
|
||||
[Serialize(3.0f, true, description: "How quickly the door closes."), Editable]
|
||||
[Serialize(3.0f, IsPropertySaveable.Yes, description: "How quickly the door closes."), Editable]
|
||||
public float ClosingSpeed { get; private set; }
|
||||
|
||||
[Serialize(1.0f, true, description: "The door cannot be opened/closed during this time after it has been opened/closed by another character."), Editable]
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes, description: "The door cannot be opened/closed during this time after it has been opened/closed by another character."), Editable]
|
||||
public float ToggleCoolDown { get; private set; }
|
||||
|
||||
public bool? PredictedState { get; private set; }
|
||||
@@ -155,10 +155,10 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public bool IsHorizontal { get; private set; }
|
||||
|
||||
[Serialize("0.0,0.0,0.0,0.0", false, description: "Position and size of the window on the door. The upper left corner is 0,0. Set the width and height to 0 if you don't want the door to have a window.")]
|
||||
[Serialize("0.0,0.0,0.0,0.0", IsPropertySaveable.No, description: "Position and size of the window on the door. The upper left corner is 0,0. Set the width and height to 0 if you don't want the door to have a window.")]
|
||||
public Rectangle Window { get; set; }
|
||||
|
||||
[Editable, Serialize(false, true, description: "Is the door currently open.")]
|
||||
[Editable, Serialize(false, IsPropertySaveable.Yes, description: "Is the door currently open.")]
|
||||
public bool IsOpen
|
||||
{
|
||||
get { return isOpen; }
|
||||
@@ -169,7 +169,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "If the door has integrated buttons, it can be opened by interacting with it directly (instead of using buttons wired to it).")]
|
||||
[Serialize(false, IsPropertySaveable.No, description: "If the door has integrated buttons, it can be opened by interacting with it directly (instead of using buttons wired to it).")]
|
||||
public bool HasIntegratedButtons { get; private set; }
|
||||
|
||||
public float OpenState
|
||||
@@ -187,39 +187,39 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "Characters and items cannot pass through impassable doors. Useful for things such as ducts that should only let water and air through.")]
|
||||
[Serialize(false, IsPropertySaveable.No, description: "Characters and items cannot pass through impassable doors. Useful for things such as ducts that should only let water and air through.")]
|
||||
public bool Impassable
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(true, true, description: "", alwaysUseInstanceValues: true)]
|
||||
[Editable, Serialize(true, IsPropertySaveable.Yes, description: "", alwaysUseInstanceValues: true)]
|
||||
public bool UseBetweenOutpostModules { get; private set; }
|
||||
|
||||
[Editable, Serialize(false, false, description: "If true, bots won't try to close this door behind them.", alwaysUseInstanceValues: true)]
|
||||
[Editable, Serialize(false, IsPropertySaveable.No, description: "If true, bots won't try to close this door behind them.", alwaysUseInstanceValues: true)]
|
||||
public bool BotsShouldKeepOpen { get; private set; }
|
||||
|
||||
public Door(Item item, XElement element)
|
||||
public Door(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsHorizontal = element.GetAttributeBool("horizontal", false);
|
||||
canBePicked = element.GetAttributeBool("canbepicked", false);
|
||||
autoOrientGap = element.GetAttributeBool("autoorientgap", false);
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
string texturePath = subElement.GetAttributeString("texture", "");
|
||||
string textureDir = GetTextureDirectory(subElement);
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "sprite":
|
||||
doorSprite = new Sprite(subElement, texturePath.Contains("/") ? "" : Path.GetDirectoryName(item.Prefab.FilePath));
|
||||
doorSprite = new Sprite(subElement, path: textureDir);
|
||||
break;
|
||||
case "weldedsprite":
|
||||
weldedSprite = new Sprite(subElement, texturePath.Contains("/") ? "" : Path.GetDirectoryName(item.Prefab.FilePath));
|
||||
weldedSprite = new Sprite(subElement, path: textureDir);
|
||||
break;
|
||||
case "brokensprite":
|
||||
brokenSprite = new Sprite(subElement, texturePath.Contains("/") ? "" : Path.GetDirectoryName(item.Prefab.FilePath));
|
||||
brokenSprite = new Sprite(subElement, path: textureDir);
|
||||
scaleBrokenSprite = subElement.GetAttributeBool("scale", false);
|
||||
fadeBrokenSprite = subElement.GetAttributeBool("fade", false);
|
||||
break;
|
||||
@@ -269,15 +269,15 @@ namespace Barotrauma.Items.Components
|
||||
#endif
|
||||
}
|
||||
|
||||
private readonly string accessDeniedTxt = TextManager.Get("AccessDenied");
|
||||
private readonly string cannotOpenText = TextManager.Get("DoorMsgCannotOpen");
|
||||
public override bool HasRequiredItems(Character character, bool addMessage, string msg = null)
|
||||
private readonly LocalizedString accessDeniedTxt = TextManager.Get("AccessDenied");
|
||||
private readonly LocalizedString cannotOpenText = TextManager.Get("DoorMsgCannotOpen");
|
||||
public override bool HasRequiredItems(Character character, bool addMessage, LocalizedString msg = null)
|
||||
{
|
||||
Msg = HasAccess(character) ? "ItemMsgOpen" : "ItemMsgForceOpenCrowbar";
|
||||
ParseMsg();
|
||||
if (addMessage)
|
||||
{
|
||||
msg = msg ?? (HasIntegratedButtons ? accessDeniedTxt : cannotOpenText);
|
||||
msg = msg ?? (HasIntegratedButtons ? accessDeniedTxt : cannotOpenText).Value;
|
||||
}
|
||||
return isBroken || base.HasRequiredItems(character, addMessage, msg);
|
||||
}
|
||||
@@ -337,7 +337,7 @@ namespace Barotrauma.Items.Components
|
||||
#if CLIENT
|
||||
else if (hasRequiredItems && character != null && character == Character.Controlled)
|
||||
{
|
||||
GUI.AddMessage(accessDeniedTxt, GUI.Style.Red);
|
||||
GUI.AddMessage(accessDeniedTxt, GUIStyle.Red);
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
@@ -561,7 +561,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (!characterPosErrorShown.Contains(c))
|
||||
{
|
||||
if (GameSettings.VerboseLogging) { DebugConsole.ThrowError("Failed to push a character out of a doorway - position of the character \"" + c.Name + "\" is not valid (" + c.SimPosition + ")"); }
|
||||
if (GameSettings.CurrentConfig.VerboseLogging) { DebugConsole.ThrowError("Failed to push a character out of a doorway - position of the character \"" + c.Name + "\" is not valid (" + c.SimPosition + ")"); }
|
||||
GameAnalyticsManager.AddErrorEventOnce("PushCharactersAway:CharacterPosInvalid", GameAnalyticsManager.ErrorSeverity.Error,
|
||||
"Failed to push a character out of a doorway - position of the character \"" + c.SpeciesName + "\" is not valid (" + c.SimPosition + ")." +
|
||||
" Removed: " + c.Removed +
|
||||
|
||||
@@ -48,28 +48,28 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(500.0f, true, description: "How far the discharge can travel from the item.", alwaysUseInstanceValues: true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 5000.0f)]
|
||||
[Serialize(500.0f, IsPropertySaveable.Yes, description: "How far the discharge can travel from the item.", alwaysUseInstanceValues: true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 5000.0f)]
|
||||
public float Range
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(25.0f, true, description: "How much further can the discharge be carried when moving across walls.", alwaysUseInstanceValues: true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f)]
|
||||
[Serialize(25.0f, IsPropertySaveable.Yes, description: "How much further can the discharge be carried when moving across walls.", alwaysUseInstanceValues: true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f)]
|
||||
public float RangeMultiplierInWalls
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(0.25f, true, description: "The duration of an individual discharge (in seconds)."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 60.0f, ValueStep = 0.1f, DecimalCount = 2)]
|
||||
[Serialize(0.25f, IsPropertySaveable.Yes, description: "The duration of an individual discharge (in seconds)."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 60.0f, ValueStep = 0.1f, DecimalCount = 2)]
|
||||
public float Duration
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, true, "If set to true, the discharge cannot travel inside the submarine nor shock anyone inside."), Editable]
|
||||
[Serialize(false, IsPropertySaveable.Yes, "If set to true, the discharge cannot travel inside the submarine nor shock anyone inside."), Editable]
|
||||
public bool OutdoorsOnly
|
||||
{
|
||||
get;
|
||||
@@ -90,12 +90,12 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private readonly Attack attack;
|
||||
|
||||
public ElectricalDischarger(Item item, XElement element) :
|
||||
public ElectricalDischarger(Item item, ContentXElement element) :
|
||||
base(item, element)
|
||||
{
|
||||
list.Add(this);
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
@@ -119,6 +119,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
CurrPowerConsumption = powerConsumption;
|
||||
Voltage = 0.0f;
|
||||
|
||||
charging = true;
|
||||
timer = Duration;
|
||||
IsActive = true;
|
||||
@@ -142,10 +143,10 @@ namespace Barotrauma.Items.Components
|
||||
timer -= deltaTime;
|
||||
if (charging)
|
||||
{
|
||||
if (GetAvailableInstantaneousBatteryPower() >= powerConsumption)
|
||||
if (GetAvailableInstantaneousBatteryPower() >= PowerConsumption)
|
||||
{
|
||||
var batteries = item.GetConnectedComponents<PowerContainer>();
|
||||
float neededPower = powerConsumption;
|
||||
List<PowerContainer> batteries = GetConnectedBatteries();
|
||||
float neededPower = PowerConsumption;
|
||||
while (neededPower > 0.0001f && batteries.Count > 0)
|
||||
{
|
||||
batteries.RemoveAll(b => b.Charge <= 0.0001f || b.MaxOutPut <= 0.0001f);
|
||||
@@ -170,6 +171,14 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Discharge coil only draws power when charging
|
||||
/// </summary>
|
||||
public override float GetCurrentPowerConsumption(Connection connection = null)
|
||||
{
|
||||
return charging && IsActive ? PowerConsumption : 0;
|
||||
}
|
||||
|
||||
public override void UpdateBroken(float deltaTime, Camera cam)
|
||||
{
|
||||
base.UpdateBroken(deltaTime, cam);
|
||||
@@ -498,7 +507,7 @@ namespace Barotrauma.Items.Components
|
||||
list.Remove(this);
|
||||
}
|
||||
|
||||
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
|
||||
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
//no further data needed, the event just triggers the discharge
|
||||
}
|
||||
|
||||
+27
-28
@@ -16,55 +16,55 @@ namespace Barotrauma.Items.Components
|
||||
Circle
|
||||
}
|
||||
|
||||
[Editable, Serialize("", true, "Identifier of the item to spawn, does nothing if SpeciesName is set. Separate by comma to have multiple items spawn at random.")]
|
||||
[Editable, Serialize("", IsPropertySaveable.Yes, "Identifier of the item to spawn, does nothing if SpeciesName is set. Separate by comma to have multiple items spawn at random.")]
|
||||
public string? ItemIdentifier { get; set; }
|
||||
|
||||
[Editable, Serialize("", true, "Species name of the creature to spawn, takes priority if ItemIdentifier is set. Separate by comma to have multiple creatures spawn at random.")]
|
||||
[Editable, Serialize("", IsPropertySaveable.Yes, "Species name of the creature to spawn, takes priority if ItemIdentifier is set. Separate by comma to have multiple creatures spawn at random.")]
|
||||
public string? SpeciesName { get; set; }
|
||||
|
||||
[Editable, Serialize(true, true, "Only spawn if crew members are within certain area")]
|
||||
[Editable, Serialize(true, IsPropertySaveable.Yes, "Only spawn if crew members are within certain area")]
|
||||
public bool OnlySpawnWhenCrewInRange { get; set; }
|
||||
|
||||
[Editable, Serialize(AreaShape.Rectangle, true, "Shape of the area where crew members need to stay")]
|
||||
[Editable, Serialize(AreaShape.Rectangle, IsPropertySaveable.Yes, "Shape of the area where crew members need to stay")]
|
||||
public AreaShape CrewAreaShape { get; set; }
|
||||
|
||||
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = 0, ValueStep = 10f), Serialize("500,500", true, "Size of the rectangle where crew members need to stay. Does nothing if CrewAreaShape is set to Circle")]
|
||||
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = 0, ValueStep = 10f), Serialize("500,500", IsPropertySaveable.Yes, "Size of the rectangle where crew members need to stay. Does nothing if CrewAreaShape is set to Circle")]
|
||||
public Vector2 CrewAreaBounds { get; set; }
|
||||
|
||||
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = 0, ValueStep = 10f), Serialize(500f, true, "Radius of the circle to spawn stuff in. Does nothing if CrewAreaShape is set to Rectangle")]
|
||||
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = 0, ValueStep = 10f), Serialize(500f, IsPropertySaveable.Yes, "Radius of the circle to spawn stuff in. Does nothing if CrewAreaShape is set to Rectangle")]
|
||||
public float CrewAreaRadius { get; set; }
|
||||
|
||||
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = int.MinValue, ValueStep = 10f), Serialize("0,0", true, "Offset of the crew area from the center of the item")]
|
||||
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = int.MinValue, ValueStep = 10f), Serialize("0,0", IsPropertySaveable.Yes, "Offset of the crew area from the center of the item")]
|
||||
public Vector2 CrewAreaOffset { get; set; }
|
||||
|
||||
[Editable, Serialize(AreaShape.Rectangle, true, "Shape of the area where enemies or items are spawned")]
|
||||
[Editable, Serialize(AreaShape.Rectangle, IsPropertySaveable.Yes, "Shape of the area where enemies or items are spawned")]
|
||||
public AreaShape SpawnAreaShape { get; set; }
|
||||
|
||||
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = 0, ValueStep = 10f), Serialize("500,500", true, "Size of the rectangle where items or creatures will be spawned. Does nothing if SpawnAreaShape is set to Circle")]
|
||||
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = 0, ValueStep = 10f), Serialize("500,500", IsPropertySaveable.Yes, "Size of the rectangle where items or creatures will be spawned. Does nothing if SpawnAreaShape is set to Circle")]
|
||||
public Vector2 SpawnAreaBounds { get; set; }
|
||||
|
||||
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = 0, ValueStep = 10f), Serialize(500f, true, "Radius of the circle where items or creatures will be spawned. Does nothing if SpawnAreaShape is set to Rectangle")]
|
||||
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = 0, ValueStep = 10f), Serialize(500f, IsPropertySaveable.Yes, "Radius of the circle where items or creatures will be spawned. Does nothing if SpawnAreaShape is set to Rectangle")]
|
||||
public float SpawnAreaRadius { get; set; }
|
||||
|
||||
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = int.MinValue, ValueStep = 10f), Serialize("0,0", true, "Offset of the spawn area from the center of the item")]
|
||||
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = int.MinValue, ValueStep = 10f), Serialize("0,0", IsPropertySaveable.Yes, "Offset of the spawn area from the center of the item")]
|
||||
public Vector2 SpawnAreaOffset { get; set; }
|
||||
|
||||
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = int.MinValue, ValueStep = 1f), Serialize("10,40", true, "Time range between spawn attempts in seconds. Set both to a negative value to disable automatic spawning.")]
|
||||
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = int.MinValue, ValueStep = 1f), Serialize("10,40", IsPropertySaveable.Yes, "Time range between spawn attempts in seconds. Set both to a negative value to disable automatic spawning.")]
|
||||
public Vector2 SpawnTimerRange { get; set; }
|
||||
|
||||
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = 1f, ValueStep = 1f, DecimalCount = 0), Serialize("1,3", true, "Minumum and maximum amount of items or creatures to spawn in one attempt")]
|
||||
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = 1f, ValueStep = 1f, DecimalCount = 0), Serialize("1,3", IsPropertySaveable.Yes, "Minumum and maximum amount of items or creatures to spawn in one attempt")]
|
||||
public Vector2 SpawnAmountRange { get; set; }
|
||||
|
||||
[Editable(MinValueInt = 0, MaxValueInt = int.MaxValue), Serialize(8, true, "Total maximum amount of items or creatures that can be spawned. 0 = unrestricted.")]
|
||||
[Editable(MinValueInt = 0, MaxValueInt = int.MaxValue), Serialize(8, IsPropertySaveable.Yes, "Total maximum amount of items or creatures that can be spawned. 0 = unrestricted.")]
|
||||
public int MaximumAmount { get; set; }
|
||||
|
||||
[Editable(MinValueInt = 0, MaxValueInt = int.MaxValue), Serialize(8, true, "Amount of items or creatures in the spawn area that will prevent further items or creatures from being spawned. 0 = unrestricted.")]
|
||||
[Editable(MinValueInt = 0, MaxValueInt = int.MaxValue), Serialize(8, IsPropertySaveable.Yes, "Amount of items or creatures in the spawn area that will prevent further items or creatures from being spawned. 0 = unrestricted.")]
|
||||
public int MaximumAmountInArea { get; set; }
|
||||
|
||||
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = 0, ValueStep = 10f), Serialize(500f, true, "Inflate the circle of rectangle by this value to extend the area that counts towards the maximum amount of items or enemies to be spawned")]
|
||||
[Editable(MaxValueFloat = int.MaxValue, MinValueFloat = 0, ValueStep = 10f), Serialize(500f, IsPropertySaveable.Yes, "Inflate the circle of rectangle by this value to extend the area that counts towards the maximum amount of items or enemies to be spawned")]
|
||||
public float MaximumAmountRangePadding { get; set; }
|
||||
|
||||
[Serialize(true, true, "")]
|
||||
[Serialize(true, IsPropertySaveable.Yes, "")]
|
||||
public bool CanSpawn { get; set; } = true;
|
||||
|
||||
private float spawnTimer;
|
||||
@@ -72,7 +72,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private int spawnedAmount = 0;
|
||||
|
||||
public EntitySpawnerComponent(Item item, XElement element) : base(item, element)
|
||||
public EntitySpawnerComponent(Item item, ContentXElement element) : base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
}
|
||||
@@ -90,7 +90,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
foreach (ItemPrefab prefab in ItemPrefab.Prefabs)
|
||||
{
|
||||
if (string.Equals(trimmedString, prefab.Identifier, StringComparison.OrdinalIgnoreCase))
|
||||
if (trimmedString == prefab.Identifier)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
@@ -182,11 +182,11 @@ namespace Barotrauma.Items.Components
|
||||
int amount;
|
||||
if (!string.IsNullOrWhiteSpace(SpeciesName))
|
||||
{
|
||||
amount = Character.CharacterList.Count(c => !c.IsDead && c.SpeciesName.Equals(SpeciesName, StringComparison.OrdinalIgnoreCase) && IsInRange(c.WorldPosition, crewArea: false, rangePad: true));
|
||||
amount = Character.CharacterList.Count(c => !c.IsDead && c.SpeciesName == SpeciesName && IsInRange(c.WorldPosition, crewArea: false, rangePad: true));
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(ItemIdentifier))
|
||||
{
|
||||
amount = Item.ItemList.Count(it => it.Submarine == item.Submarine && it.Prefab.Identifier.Equals(ItemIdentifier, StringComparison.OrdinalIgnoreCase) && IsInRange(it.WorldPosition, crewArea: false, rangePad: true));
|
||||
amount = Item.ItemList.Count(it => it.Submarine == item.Submarine && it.Prefab.Identifier == ItemIdentifier && IsInRange(it.WorldPosition, crewArea: false, rangePad: true));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -229,7 +229,6 @@ namespace Barotrauma.Items.Components
|
||||
int amount = Rand.Range(minAmount, maxAmount, Rand.RandSync.Unsynced);
|
||||
|
||||
Vector2 offset = SpawnAreaOffset;
|
||||
offset.Y = -offset.Y;
|
||||
|
||||
switch (SpawnAreaShape)
|
||||
{
|
||||
@@ -270,15 +269,15 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(SpeciesName))
|
||||
{
|
||||
string[] allSpecies = SpeciesName.Split(',');
|
||||
string species = allSpecies.GetRandom().Trim();
|
||||
Entity.Spawner?.AddToSpawnQueue(species, pos);
|
||||
Identifier[] allSpecies = SpeciesName.Split(',').Select(s => s.Trim()).ToIdentifiers().ToArray();
|
||||
Identifier species = allSpecies.GetRandomUnsynced();
|
||||
Entity.Spawner?.AddCharacterToSpawnQueue(species, pos);
|
||||
spawnedAmount++;
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(ItemIdentifier))
|
||||
{
|
||||
string[] allItems = ItemIdentifier.Split(',');
|
||||
string itemIdentifier = allItems.GetRandom().Trim();
|
||||
Identifier[] allItems = ItemIdentifier.Split(',').Select(s => s.Trim()).ToIdentifiers().ToArray();
|
||||
Identifier itemIdentifier = allItems.GetRandomUnsynced();
|
||||
ItemPrefab? prefab = ItemPrefab.Find(null, itemIdentifier);
|
||||
if (prefab is null) { return; }
|
||||
|
||||
@@ -287,7 +286,7 @@ namespace Barotrauma.Items.Components
|
||||
pos -= sub.Position;
|
||||
}
|
||||
|
||||
Entity.Spawner?.AddToSpawnQueue(prefab, pos, item.Submarine);
|
||||
Entity.Spawner?.AddItemToSpawnQueue(prefab, pos, item.Submarine);
|
||||
spawnedAmount++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,27 +10,27 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class GeneticMaterial : ItemComponent, IServerSerializable
|
||||
{
|
||||
private readonly string materialName;
|
||||
private readonly LocalizedString materialName;
|
||||
|
||||
private Character targetCharacter;
|
||||
private AfflictionPrefab selectedEffect, selectedTaintedEffect;
|
||||
|
||||
[Serialize("", true)]
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public string Effect
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize("geneticmaterialdebuff", true)]
|
||||
public string TaintedEffect
|
||||
[Serialize("geneticmaterialdebuff", IsPropertySaveable.Yes)]
|
||||
public Identifier TaintedEffect
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
private bool tainted;
|
||||
[Serialize(false, true)]
|
||||
[Serialize(false, IsPropertySaveable.Yes)]
|
||||
public bool Tainted
|
||||
{
|
||||
get { return tainted; }
|
||||
@@ -39,28 +39,27 @@ namespace Barotrauma.Items.Components
|
||||
if (!value) { return; }
|
||||
tainted = true;
|
||||
item.AllowDeconstruct = false;
|
||||
if (!string.IsNullOrEmpty(TaintedEffect))
|
||||
if (!TaintedEffect.IsEmpty)
|
||||
{
|
||||
selectedTaintedEffect = AfflictionPrefab.Prefabs.Where(a =>
|
||||
a.Identifier.Equals(TaintedEffect, StringComparison.OrdinalIgnoreCase) ||
|
||||
a.AfflictionType.Equals(TaintedEffect, StringComparison.OrdinalIgnoreCase)).GetRandom();
|
||||
a.Identifier == TaintedEffect ||
|
||||
a.AfflictionType == TaintedEffect).GetRandomUnsynced();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//only for saving the selected tainted effect
|
||||
[Serialize("", true)]
|
||||
public string SelectedTaintedEffect
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public Identifier SelectedTaintedEffect
|
||||
{
|
||||
get { return selectedTaintedEffect?.Identifier ?? string.Empty; }
|
||||
get { return selectedTaintedEffect?.Identifier ?? Identifier.Empty; }
|
||||
private set
|
||||
{
|
||||
if (string.IsNullOrEmpty(value)) { return; }
|
||||
selectedTaintedEffect = AfflictionPrefab.Prefabs.Find(a => a.Identifier == value);
|
||||
selectedTaintedEffect = !value.IsEmpty ? AfflictionPrefab.Prefabs.Find(a => a.Identifier == value) : null;
|
||||
}
|
||||
}
|
||||
|
||||
public GeneticMaterial(Item item, XElement element)
|
||||
public GeneticMaterial(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
string nameId = element.GetAttributeString("nameidentifier", "");
|
||||
@@ -71,15 +70,15 @@ namespace Barotrauma.Items.Components
|
||||
if (!string.IsNullOrEmpty(Effect))
|
||||
{
|
||||
selectedEffect = AfflictionPrefab.Prefabs.Where(a =>
|
||||
a.Identifier.Equals(Effect, StringComparison.OrdinalIgnoreCase) ||
|
||||
a.AfflictionType.Equals(Effect, StringComparison.OrdinalIgnoreCase)).GetRandom();
|
||||
a.Identifier == Effect ||
|
||||
a.AfflictionType == Effect).GetRandomUnsynced();
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(3.0f, false)]
|
||||
[Serialize(3.0f, IsPropertySaveable.No)]
|
||||
public float ConditionIncreaseOnCombineMin { get; set; }
|
||||
|
||||
[Serialize(8.0f, false)]
|
||||
[Serialize(8.0f, IsPropertySaveable.No)]
|
||||
public float ConditionIncreaseOnCombineMax { get; set; }
|
||||
|
||||
public bool CanBeCombinedWith(GeneticMaterial otherGeneticMaterial)
|
||||
@@ -230,16 +229,16 @@ namespace Barotrauma.Items.Components
|
||||
#endif
|
||||
}
|
||||
|
||||
public static string TryCreateName(ItemPrefab prefab, XElement element)
|
||||
public static LocalizedString TryCreateName(ItemPrefab prefab, XElement element)
|
||||
{
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (subElement.Name.ToString().Equals(nameof(GeneticMaterial), StringComparison.OrdinalIgnoreCase))
|
||||
if (subElement.NameAsIdentifier() == nameof(GeneticMaterial))
|
||||
{
|
||||
string nameId = subElement.GetAttributeString("nameidentifier", "");
|
||||
if (!string.IsNullOrEmpty(nameId))
|
||||
Identifier nameId = subElement.GetAttributeIdentifier("nameidentifier", "");
|
||||
if (!nameId.IsEmpty)
|
||||
{
|
||||
return prefab.Name.Replace("[type]", TextManager.Get(nameId, returnNull: true) ?? nameId);
|
||||
return prefab.Name.Replace("[type]", TextManager.Get(nameId).Fallback(nameId.Value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,25 +15,30 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
internal class ProducedItem
|
||||
{
|
||||
[Serialize(0f, true)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes)]
|
||||
public float Probability { get; set; }
|
||||
|
||||
public readonly List<StatusEffect> StatusEffects = new List<StatusEffect>();
|
||||
|
||||
public readonly Item Producer;
|
||||
|
||||
public readonly ItemPrefab? Prefab;
|
||||
|
||||
public ProducedItem(ItemPrefab prefab, float probability)
|
||||
public ProducedItem(Item producer, ItemPrefab prefab, float probability)
|
||||
{
|
||||
Producer = producer;
|
||||
Prefab = prefab;
|
||||
Probability = probability;
|
||||
}
|
||||
|
||||
public ProducedItem(XElement element)
|
||||
public ProducedItem(Item producer, ContentXElement element)
|
||||
{
|
||||
SerializableProperty.DeserializeProperties(this, element);
|
||||
|
||||
string itemIdentifier = element.GetAttributeString("identifier", string.Empty);
|
||||
if (!string.IsNullOrWhiteSpace(itemIdentifier))
|
||||
Producer = producer;
|
||||
|
||||
Identifier itemIdentifier = element.GetAttributeIdentifier("identifier", Identifier.Empty);
|
||||
if (!itemIdentifier.IsEmpty)
|
||||
{
|
||||
Prefab = ItemPrefab.Find(null, itemIdentifier);
|
||||
}
|
||||
@@ -41,17 +46,17 @@ namespace Barotrauma.Items.Components
|
||||
LoadSubElements(element);
|
||||
}
|
||||
|
||||
private void LoadSubElements(XElement element)
|
||||
private void LoadSubElements(ContentXElement element)
|
||||
{
|
||||
if (!element.HasElements) { return; }
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "statuseffect":
|
||||
{
|
||||
StatusEffect effect = StatusEffect.Load(subElement, Prefab?.Name);
|
||||
StatusEffect effect = StatusEffect.Load(subElement, Prefab?.Name.Value);
|
||||
if (effect.type != ActionType.OnProduceSpawned)
|
||||
{
|
||||
DebugConsole.ThrowError("Only OnProduceSpawned type can be used in <ProducedItem>.");
|
||||
@@ -325,9 +330,14 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
public static TileSide GetOppositeSide(this TileSide side)
|
||||
{
|
||||
return (TileSide) (1 << ((int) Math.Log2((int) side) + 2) % 4);
|
||||
}
|
||||
=> side switch
|
||||
{
|
||||
TileSide.Left => TileSide.Right,
|
||||
TileSide.Right => TileSide.Left,
|
||||
TileSide.Bottom => TileSide.Top,
|
||||
TileSide.Top => TileSide.Bottom,
|
||||
_ => throw new ArgumentException($"Expected Left, Right, Bottom or Top, got {side}")
|
||||
};
|
||||
}
|
||||
|
||||
internal partial class Growable : ItemComponent, IServerSerializable
|
||||
@@ -335,61 +345,61 @@ namespace Barotrauma.Items.Components
|
||||
// used for debugging where a vine failed to grow
|
||||
public readonly HashSet<Rectangle> FailedRectangles = new HashSet<Rectangle>();
|
||||
|
||||
[Serialize(1f, true, "How fast the plant grows.")]
|
||||
[Serialize(1f, IsPropertySaveable.Yes, "How fast the plant grows.")]
|
||||
public float GrowthSpeed { get; set; }
|
||||
|
||||
[Serialize(100f, true, "How long the plant can go without watering.")]
|
||||
[Serialize(100f, IsPropertySaveable.Yes, "How long the plant can go without watering.")]
|
||||
public float MaxHealth { get; set; }
|
||||
|
||||
[Serialize(1f, true, "How much damage the plant takes while in water.")]
|
||||
[Serialize(1f, IsPropertySaveable.Yes, "How much damage the plant takes while in water.")]
|
||||
public float FloodTolerance { get; set; }
|
||||
|
||||
[Serialize(1f, true, "How much damage the plant takes while growing.")]
|
||||
[Serialize(1f, IsPropertySaveable.Yes, "How much damage the plant takes while growing.")]
|
||||
public float Hardiness { get; set; }
|
||||
|
||||
[Serialize(0.01f, true, "How often a seed is produced.")]
|
||||
[Serialize(0.01f, IsPropertySaveable.Yes, "How often a seed is produced.")]
|
||||
public float SeedRate { get; set; }
|
||||
|
||||
[Serialize(0.01f, true, "How often a product item is produced.")]
|
||||
[Serialize(0.01f, IsPropertySaveable.Yes, "How often a product item is produced.")]
|
||||
public float ProductRate { get; set; }
|
||||
|
||||
[Serialize(0.5f, true, "Probability of an attribute being randomly modified in a newly produced seed.")]
|
||||
[Serialize(0.5f, IsPropertySaveable.Yes, "Probability of an attribute being randomly modified in a newly produced seed.")]
|
||||
public float MutationProbability { get; set; }
|
||||
|
||||
[Serialize("1.0,1.0,1.0,1.0", true, "Color of the flowers.")]
|
||||
[Serialize("1.0,1.0,1.0,1.0", IsPropertySaveable.Yes, "Color of the flowers.")]
|
||||
public Color FlowerTint { get; set; }
|
||||
|
||||
[Serialize(3, true, "Number of flowers drawn when fully grown")]
|
||||
[Serialize(3, IsPropertySaveable.Yes, "Number of flowers drawn when fully grown")]
|
||||
public int FlowerQuantity { get; set; }
|
||||
|
||||
[Serialize(0.25f, true, "Size of the flower sprites.")]
|
||||
[Serialize(0.25f, IsPropertySaveable.Yes, "Size of the flower sprites.")]
|
||||
public float BaseFlowerScale { get; set; }
|
||||
|
||||
[Serialize(0.5f, true, "Size of the leaf sprites.")]
|
||||
[Serialize(0.5f, IsPropertySaveable.Yes, "Size of the leaf sprites.")]
|
||||
public float BaseLeafScale { get; set; }
|
||||
|
||||
[Serialize("1.0,1.0,1.0,1.0", true, "Color of the leaves.")]
|
||||
[Serialize("1.0,1.0,1.0,1.0", IsPropertySaveable.Yes, "Color of the leaves.")]
|
||||
public Color LeafTint { get; set; }
|
||||
|
||||
[Serialize(0.33f, true, "Chance of a leaf appearing behind a branch.")]
|
||||
[Serialize(0.33f, IsPropertySaveable.Yes, "Chance of a leaf appearing behind a branch.")]
|
||||
public float LeafProbability { get; set; }
|
||||
|
||||
[Serialize("1.0,1.0,1.0,1.0", true, "Color of the vines.")]
|
||||
[Serialize("1.0,1.0,1.0,1.0", IsPropertySaveable.Yes, "Color of the vines.")]
|
||||
public Color VineTint { get; set; }
|
||||
|
||||
[Serialize(32, true, "Maximum number of vine tiles the plant can grow.")]
|
||||
[Serialize(32, IsPropertySaveable.Yes, "Maximum number of vine tiles the plant can grow.")]
|
||||
public int MaximumVines { get; set; }
|
||||
|
||||
[Serialize(0.25f, true, "Size of the vine sprites.")]
|
||||
[Serialize(0.25f, IsPropertySaveable.Yes, "Size of the vine sprites.")]
|
||||
public float VineScale { get; set; }
|
||||
|
||||
[Serialize("0.26,0.27,0.29,1.0", true, "Tint of a dead plant.")]
|
||||
[Serialize("0.26,0.27,0.29,1.0", IsPropertySaveable.Yes, "Tint of a dead plant.")]
|
||||
public Color DeadTint { get; set; }
|
||||
|
||||
[Serialize("1,1,1,1", true, "Probability for the plant to grow in a direction.")]
|
||||
[Serialize("1,1,1,1", IsPropertySaveable.Yes, "Probability for the plant to grow in a direction.")]
|
||||
public Vector4 GrowthWeights { get; set; }
|
||||
|
||||
[Serialize(0.0f, true, "How much damage is taken from fires.")]
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes, "How much damage is taken from fires.")]
|
||||
public float FireVulnerability { get; set; }
|
||||
|
||||
private const float increasedDeathSpeed = 10f;
|
||||
@@ -422,7 +432,7 @@ namespace Barotrauma.Items.Components
|
||||
private static float MinFlowerScale = 0.5f, MaxFlowerScale = 1.0f, MinLeafScale = 0.5f, MaxLeafScale = 1.0f;
|
||||
private const int VineChunkSize = 32;
|
||||
|
||||
public Growable(Item item, XElement element) : base(item, element)
|
||||
public Growable(Item item, ContentXElement element) : base(item, element)
|
||||
{
|
||||
SerializableProperty.DeserializeProperties(this, element);
|
||||
|
||||
@@ -430,12 +440,12 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (element.HasElements)
|
||||
{
|
||||
foreach (XElement subElement in element.Elements())
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "produceditem":
|
||||
ProducedItems.Add(new ProducedItem(subElement));
|
||||
ProducedItems.Add(new ProducedItem(this.item, subElement));
|
||||
break;
|
||||
case "vinesprites":
|
||||
LoadVines(subElement);
|
||||
@@ -444,7 +454,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
ProducedSeed = new ProducedItem(this.item.Prefab, 1.0f);
|
||||
ProducedSeed = new ProducedItem(this.item, this.item.Prefab, 1.0f);
|
||||
flowerTiles = new int[FlowerQuantity];
|
||||
}
|
||||
|
||||
@@ -471,7 +481,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
partial void LoadVines(XElement element);
|
||||
partial void LoadVines(ContentXElement element);
|
||||
|
||||
public void OnGrowthTick(Planter planter, PlantSlot slot)
|
||||
{
|
||||
@@ -541,7 +551,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (spawnProduct || spawnSeed)
|
||||
{
|
||||
VineTile vine = Vines.GetRandom();
|
||||
VineTile vine = Vines.GetRandomUnsynced();
|
||||
spawnPos = vine.GetWorldPosition(planter, slot.Offset);
|
||||
}
|
||||
else
|
||||
@@ -564,9 +574,9 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (producedItem.Prefab == null) { return; }
|
||||
|
||||
GameAnalyticsManager.AddDesignEvent("MicroInteraction:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "null") + ":GardeningProduce:" + thisItem.prefab.Identifier + ":" + producedItem.Prefab.Identifier);
|
||||
GameAnalyticsManager.AddDesignEvent("MicroInteraction:" + (GameMain.GameSession?.GameMode?.Preset.Identifier.Value ?? "null") + ":GardeningProduce:" + thisItem.Prefab.Identifier + ":" + producedItem.Prefab.Identifier);
|
||||
|
||||
Entity.Spawner?.AddToSpawnQueue(producedItem.Prefab, pos, onSpawned: it =>
|
||||
Entity.Spawner?.AddItemToSpawnQueue(producedItem.Prefab, pos, onSpawned: it =>
|
||||
{
|
||||
foreach (StatusEffect effect in producedItem.StatusEffects)
|
||||
{
|
||||
@@ -590,7 +600,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (!Decayed)
|
||||
{
|
||||
GameAnalyticsManager.AddDesignEvent("MicroInteraction:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "null") + ":GardeningDied:" + item.prefab.Identifier);
|
||||
GameAnalyticsManager.AddDesignEvent("MicroInteraction:" + (GameMain.GameSession?.GameMode?.Preset.Identifier.Value ?? "null") + ":GardeningDied:" + item.Prefab.Identifier);
|
||||
}
|
||||
|
||||
Decayed = true;
|
||||
@@ -697,7 +707,7 @@ namespace Barotrauma.Items.Components
|
||||
#if SERVER
|
||||
for (int i = 0; i < Vines.Count; i += VineChunkSize)
|
||||
{
|
||||
GameMain.Server.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ComponentState, item.GetComponentIndex(this), i });
|
||||
item.CreateServerEvent(this, new EventData(offset: i));
|
||||
}
|
||||
#elif CLIENT
|
||||
ResetPlanterSize();
|
||||
@@ -881,14 +891,14 @@ namespace Barotrauma.Items.Components
|
||||
return element;
|
||||
}
|
||||
|
||||
public override void Load(XElement componentElement, bool usePrefabValues, IdRemap idRemap)
|
||||
public override void Load(ContentXElement componentElement, bool usePrefabValues, IdRemap idRemap)
|
||||
{
|
||||
base.Load(componentElement, usePrefabValues, idRemap);
|
||||
flowerTiles = componentElement.GetAttributeIntArray("flowertiles", new int[0]);
|
||||
flowerTiles = componentElement.GetAttributeIntArray("flowertiles", Array.Empty<int>())!;
|
||||
Decayed = componentElement.GetAttributeBool("decayed", false);
|
||||
|
||||
Vines.Clear();
|
||||
foreach (XElement element in componentElement.Elements())
|
||||
foreach (var element in componentElement.Elements())
|
||||
{
|
||||
if (element.Name.ToString().Equals("vine", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -7,6 +7,8 @@ using System.Xml.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma;
|
||||
using MoonSharp.Interpreter;
|
||||
using Barotrauma.IO;
|
||||
using Barotrauma.Networking;
|
||||
#if CLIENT
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Barotrauma.Sounds;
|
||||
@@ -66,26 +68,26 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public readonly XElement originalElement;
|
||||
public readonly ContentXElement originalElement;
|
||||
|
||||
protected const float CorrectionDelay = 1.0f;
|
||||
protected CoroutineHandle delayedCorrectionCoroutine;
|
||||
|
||||
[Editable, Serialize(0.0f, false, description: "How long it takes to pick up the item (in seconds).")]
|
||||
[Editable, Serialize(0.0f, IsPropertySaveable.No, description: "How long it takes to pick up the item (in seconds).")]
|
||||
public float PickingTime
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize("", false, description: "What to display on the progress bar when this item is being picked.")]
|
||||
[Serialize("", IsPropertySaveable.No, description: "What to display on the progress bar when this item is being picked.")]
|
||||
public string PickingMsg
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Dictionary<string, SerializableProperty> SerializableProperties { get; protected set; }
|
||||
public Dictionary<Identifier, SerializableProperty> SerializableProperties { get; protected set; }
|
||||
|
||||
public Action<bool> OnActiveStateChanged;
|
||||
|
||||
@@ -137,42 +139,42 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, false, description: "Can the item be picked up (or interacted with, if the pick action does something else than picking up the item).")] //Editable for doors to do their magic
|
||||
[Editable, Serialize(false, IsPropertySaveable.No, description: "Can the item be picked up (or interacted with, if the pick action does something else than picking up the item).")] //Editable for doors to do their magic
|
||||
public bool CanBePicked
|
||||
{
|
||||
get { return canBePicked; }
|
||||
set { canBePicked = value; }
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "Should the interface of the item (if it has one) be drawn when the item is equipped.")]
|
||||
[Serialize(false, IsPropertySaveable.No, description: "Should the interface of the item (if it has one) be drawn when the item is equipped.")]
|
||||
public bool DrawHudWhenEquipped
|
||||
{
|
||||
get;
|
||||
protected set;
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "Can the item be selected by interacting with it.")]
|
||||
[Serialize(false, IsPropertySaveable.No, description: "Can the item be selected by interacting with it.")]
|
||||
public bool CanBeSelected
|
||||
{
|
||||
get { return canBeSelected; }
|
||||
set { canBeSelected = value; }
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "Can the item be combined with other items of the same type.")]
|
||||
[Serialize(false, IsPropertySaveable.No, description: "Can the item be combined with other items of the same type.")]
|
||||
public bool CanBeCombined
|
||||
{
|
||||
get { return canBeCombined; }
|
||||
set { canBeCombined = value; }
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "Should the item be removed if combining it with an other item causes the condition of this item to drop to 0.")]
|
||||
[Serialize(false, IsPropertySaveable.No, description: "Should the item be removed if combining it with an other item causes the condition of this item to drop to 0.")]
|
||||
public bool RemoveOnCombined
|
||||
{
|
||||
get { return removeOnCombined; }
|
||||
set { removeOnCombined = value; }
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "Can the \"Use\" action of the item be triggered by characters or just other items/StatusEffects.")]
|
||||
[Serialize(false, IsPropertySaveable.No, description: "Can the \"Use\" action of the item be triggered by characters or just other items/StatusEffects.")]
|
||||
public bool CharacterUsable
|
||||
{
|
||||
get { return characterUsable; }
|
||||
@@ -180,7 +182,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
//Remove item if combination results in 0 condition
|
||||
[Serialize(true, false, description: "Can the properties of the component be edited in-game (only applicable if the component has in-game editable properties)."), Editable()]
|
||||
[Serialize(true, IsPropertySaveable.No, description: "Can the properties of the component be edited in-game (only applicable if the component has in-game editable properties)."), Editable()]
|
||||
public bool AllowInGameEditing
|
||||
{
|
||||
get;
|
||||
@@ -199,7 +201,7 @@ namespace Barotrauma.Items.Components
|
||||
protected set;
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "Should the item be deleted when it's used.")]
|
||||
[Serialize(false, IsPropertySaveable.No, description: "Should the item be deleted when it's used.")]
|
||||
public bool DeleteOnUse
|
||||
{
|
||||
get;
|
||||
@@ -216,14 +218,14 @@ namespace Barotrauma.Items.Components
|
||||
get { return name; }
|
||||
}
|
||||
|
||||
[Editable, Serialize("", true, translationTextTag: "ItemMsg", description: "A text displayed next to the item when it's highlighted (generally instructs how to interact with the item, e.g. \"[Mouse1] Pick up\").")]
|
||||
[Editable, Serialize("", IsPropertySaveable.Yes, translationTextTag: "ItemMsg", description: "A text displayed next to the item when it's highlighted (generally instructs how to interact with the item, e.g. \"[Mouse1] Pick up\").")]
|
||||
public string Msg
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public string DisplayMsg
|
||||
public LocalizedString DisplayMsg
|
||||
{
|
||||
get;
|
||||
set;
|
||||
@@ -234,16 +236,16 @@ namespace Barotrauma.Items.Components
|
||||
/// <summary>
|
||||
/// How useful the item is in combat? Used by AI to decide which item it should use as a weapon. For the sake of clarity, use a value between 0 and 100 (not enforced).
|
||||
/// </summary>
|
||||
[Serialize(0f, false, description: "How useful the item is in combat? Used by AI to decide which item it should use as a weapon. For the sake of clarity, use a value between 0 and 100 (not enforced).")]
|
||||
[Serialize(0f, IsPropertySaveable.No, description: "How useful the item is in combat? Used by AI to decide which item it should use as a weapon. For the sake of clarity, use a value between 0 and 100 (not enforced).")]
|
||||
public float CombatPriority { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Which sound should be played when manual sound selection type is selected? Not [Editable] because we don't want this visible in the editor for every component.
|
||||
/// </summary>
|
||||
[Serialize(0, true, alwaysUseInstanceValues: true)]
|
||||
[Serialize(0, IsPropertySaveable.Yes, alwaysUseInstanceValues: true)]
|
||||
public int ManuallySelectedSound { get; private set; }
|
||||
|
||||
public ItemComponent(Item item, XElement element)
|
||||
public ItemComponent(Item item, ContentXElement element)
|
||||
{
|
||||
this.item = item;
|
||||
originalElement = element;
|
||||
@@ -323,7 +325,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
@@ -344,13 +346,13 @@ namespace Barotrauma.Items.Components
|
||||
break;
|
||||
case "requiredskill":
|
||||
case "requiredskills":
|
||||
if (subElement.Attribute("name") != null)
|
||||
if (subElement.GetAttribute("name") != null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in item config \"" + item.ConfigFile + "\" - skill requirement in component " + GetType().ToString() + " should use a skill identifier instead of the name of the skill.");
|
||||
DebugConsole.ThrowError("Error in item config \"" + item.ConfigFilePath + "\" - skill requirement in component " + GetType().ToString() + " should use a skill identifier instead of the name of the skill.");
|
||||
continue;
|
||||
}
|
||||
|
||||
string skillIdentifier = subElement.GetAttributeString("identifier", "");
|
||||
Identifier skillIdentifier = subElement.GetAttributeIdentifier("identifier", "");
|
||||
requiredSkills.Add(new Skill(skillIdentifier, subElement.GetAttributeInt("level", 0)));
|
||||
break;
|
||||
case "statuseffect":
|
||||
@@ -359,7 +361,7 @@ namespace Barotrauma.Items.Components
|
||||
break;
|
||||
default:
|
||||
if (LoadElemProjSpecific(subElement)) { break; }
|
||||
ItemComponent ic = Load(subElement, item, item.ConfigFile, false);
|
||||
ItemComponent ic = Load(subElement, item, false);
|
||||
if (ic == null) { break; }
|
||||
|
||||
ic.Parent = this;
|
||||
@@ -371,7 +373,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
void LoadStatusEffect(XElement subElement)
|
||||
void LoadStatusEffect(ContentXElement subElement)
|
||||
{
|
||||
var statusEffect = StatusEffect.Load(subElement, item.Name);
|
||||
if (!statusEffectLists.TryGetValue(statusEffect.type, out List<StatusEffect> effectList))
|
||||
@@ -388,7 +390,7 @@ namespace Barotrauma.Items.Components
|
||||
IsActive = isActive;
|
||||
}
|
||||
|
||||
public void SetRequiredItems(XElement element)
|
||||
public void SetRequiredItems(ContentXElement element)
|
||||
{
|
||||
bool returnEmpty = false;
|
||||
#if CLIENT
|
||||
@@ -412,7 +414,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError("Error in item config \"" + item.ConfigFile + "\" - component " + GetType().ToString() + " requires an item with no identifiers.");
|
||||
DebugConsole.ThrowError("Error in item config \"" + item.ConfigFilePath + "\" - component " + GetType().ToString() + " requires an item with no identifiers.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -519,7 +521,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
item.ParentInventory.RemoveItem(item);
|
||||
}
|
||||
Entity.Spawner.AddToRemoveQueue(item);
|
||||
Entity.Spawner.AddItemToRemoveQueue(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -535,7 +537,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
this.Item.ParentInventory.RemoveItem(this.Item);
|
||||
}
|
||||
Entity.Spawner.AddToRemoveQueue(this.Item);
|
||||
Entity.Spawner.AddItemToRemoveQueue(this.Item);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -611,6 +613,9 @@ namespace Barotrauma.Items.Components
|
||||
protected virtual void RemoveComponentSpecific()
|
||||
{
|
||||
}
|
||||
|
||||
protected string GetTextureDirectory(ContentXElement subElement)
|
||||
=> subElement.DoesAttributeReferenceFileNameAlone("texture") ? Path.GetDirectoryName(item.Prefab.FilePath) : string.Empty;
|
||||
|
||||
public bool HasRequiredSkills(Character character)
|
||||
{
|
||||
@@ -678,7 +683,7 @@ namespace Barotrauma.Items.Components
|
||||
HasRequiredContainedItems(user, addMessage: false) &&
|
||||
(!checkContainedItems || Item.OwnInventory == null || Item.OwnInventory.AllItems.Any(i => i.Condition > 0));
|
||||
|
||||
public bool HasRequiredContainedItems(Character user, bool addMessage, string msg = null)
|
||||
public bool HasRequiredContainedItems(Character user, bool addMessage, LocalizedString msg = null)
|
||||
{
|
||||
if (!requiredItems.ContainsKey(RelatedItem.RelationType.Contained)) { return true; }
|
||||
if (item.OwnInventory == null) { return false; }
|
||||
@@ -688,8 +693,8 @@ namespace Barotrauma.Items.Components
|
||||
if (!ri.CheckRequirements(user, item))
|
||||
{
|
||||
#if CLIENT
|
||||
msg = msg ?? ri.Msg;
|
||||
if (addMessage && !string.IsNullOrEmpty(msg))
|
||||
msg ??= ri.Msg;
|
||||
if (addMessage && !msg.IsNullOrEmpty())
|
||||
{
|
||||
GUI.AddMessage(msg, Color.Red);
|
||||
}
|
||||
@@ -722,7 +727,7 @@ namespace Barotrauma.Items.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool HasRequiredItems(Character character, bool addMessage, string msg = null)
|
||||
public virtual bool HasRequiredItems(Character character, bool addMessage, LocalizedString msg = null)
|
||||
{
|
||||
if (requiredItems.None()) { return true; }
|
||||
if (character.Inventory == null) { return false; }
|
||||
@@ -748,7 +753,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
if (!hasRequiredItems && addMessage && !string.IsNullOrEmpty(msg))
|
||||
if (!hasRequiredItems && addMessage && !msg.IsNullOrEmpty())
|
||||
{
|
||||
GUI.AddMessage(msg, Color.Red);
|
||||
}
|
||||
@@ -804,7 +809,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
if (!hasRequiredItems)
|
||||
{
|
||||
if (msg == null && !string.IsNullOrEmpty(relatedItem.Msg))
|
||||
if (msg == null && !relatedItem.Msg.IsNullOrEmpty())
|
||||
{
|
||||
msg = relatedItem.Msg;
|
||||
}
|
||||
@@ -851,14 +856,14 @@ namespace Barotrauma.Items.Components
|
||||
HintManager.OnStatusEffectApplied(this, type, character);
|
||||
#endif
|
||||
}
|
||||
[MoonSharpHidden]
|
||||
public virtual void Load(XElement componentElement, bool usePrefabValues, IdRemap idRemap)
|
||||
|
||||
public virtual void Load(ContentXElement componentElement, bool usePrefabValues, IdRemap idRemap)
|
||||
{
|
||||
if (componentElement != null)
|
||||
{
|
||||
foreach (XAttribute attribute in componentElement.Attributes())
|
||||
{
|
||||
if (!SerializableProperties.TryGetValue(attribute.Name.ToString().ToLowerInvariant(), out SerializableProperty property)) { continue; }
|
||||
if (!SerializableProperties.TryGetValue(attribute.NameAsIdentifier(), out SerializableProperty property)) { continue; }
|
||||
if (property.OverridePrefabValues || !usePrefabValues)
|
||||
{
|
||||
property.TrySetValue(this, attribute.Value);
|
||||
@@ -883,43 +888,48 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public virtual void OnScaleChanged() { }
|
||||
|
||||
// TODO: Consider using generics, interfaces, or inheritance instead of reflection -> would be easier to debug when something changes/goes wrong.
|
||||
// For example, currently we can edit the constructors but they will fail in runtime because the parameters are not changed here.
|
||||
// It's also painful to find where the constructors are used, because the references exist only at runtime.
|
||||
public static ItemComponent Load(XElement element, Item item, string file, bool errorMessages = true)
|
||||
public static ItemComponent Load(ContentXElement element, Item item, bool errorMessages = true)
|
||||
{
|
||||
Type t;
|
||||
string type = element.Name.ToString().ToLowerInvariant();
|
||||
Type type;
|
||||
Identifier typeName = element.NameAsIdentifier();
|
||||
try
|
||||
{
|
||||
// Get the type of a specified class.
|
||||
t = Type.GetType("Barotrauma.Items.Components." + type + "", false, true);
|
||||
if (t == null)
|
||||
// Get the type of a specified class.
|
||||
type = ReflectionUtils.GetDerivedNonAbstract<ItemComponent>().Append(typeof(ItemComponent)).FirstOrDefault(t => t.Name == typeName);
|
||||
if (type == null)
|
||||
{
|
||||
if (errorMessages) DebugConsole.ThrowError("Could not find the component \"" + type + "\" (" + file + ")");
|
||||
if (errorMessages)
|
||||
{
|
||||
DebugConsole.ThrowError($"Could not find the component \"{typeName}\" ({item.Prefab.ContentFile.Path})");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (errorMessages) DebugConsole.ThrowError("Could not find the component \"" + type + "\" (" + file + ")", e);
|
||||
if (errorMessages)
|
||||
{
|
||||
DebugConsole.ThrowError($"Could not find the component \"{typeName}\" ({item.Prefab.ContentFile.Path})", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
ConstructorInfo constructor;
|
||||
try
|
||||
{
|
||||
if (t != typeof(ItemComponent) && !t.IsSubclassOf(typeof(ItemComponent))) return null;
|
||||
constructor = t.GetConstructor(new Type[] { typeof(Item), typeof(XElement) });
|
||||
if (type != typeof(ItemComponent) && !type.IsSubclassOf(typeof(ItemComponent))) { return null; }
|
||||
constructor = type.GetConstructor(new Type[] { typeof(Item), typeof(ContentXElement) });
|
||||
if (constructor == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Could not find the constructor of the component \"" + type + "\" (" + file + ")");
|
||||
DebugConsole.ThrowError(
|
||||
$"Could not find the constructor of the component \"{typeName}\" ({item.Prefab.ContentFile.Path})");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Could not find the constructor of the component \"" + type + "\" (" + file + ")", e);
|
||||
DebugConsole.ThrowError(
|
||||
$"Could not find the constructor of the component \"{typeName}\" ({item.Prefab.ContentFile.Path})", e);
|
||||
return null;
|
||||
}
|
||||
ItemComponent ic = null;
|
||||
@@ -932,10 +942,11 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
catch (TargetInvocationException e)
|
||||
{
|
||||
DebugConsole.ThrowError("Error while loading entity of the type " + t + ".", e.InnerException);
|
||||
GameAnalyticsManager.AddErrorEventOnce("ItemComponent.Load:TargetInvocationException" + item.Name + element.Name,
|
||||
DebugConsole.ThrowError($"Error while loading component of the type {type}.", e.InnerException);
|
||||
GameAnalyticsManager.AddErrorEventOnce(
|
||||
$"ItemComponent.Load:TargetInvocationException{item.Name}{element.Name}",
|
||||
GameAnalyticsManager.ErrorSeverity.Error,
|
||||
"Error while loading entity of the type " + t + " (" + e.InnerException + ")\n" + Environment.StackTrace.CleanupStackTrace());
|
||||
$"Error while loading entity of the type {type} ({e.InnerException})\n{Environment.StackTrace.CleanupStackTrace()}");
|
||||
}
|
||||
|
||||
return ic;
|
||||
@@ -976,7 +987,7 @@ namespace Barotrauma.Items.Components
|
||||
OverrideRequiredItems(originalElement);
|
||||
}
|
||||
|
||||
private void OverrideRequiredItems(XElement element)
|
||||
private void OverrideRequiredItems(ContentXElement element)
|
||||
{
|
||||
var prevRequiredItems = new Dictionary<RelatedItem.RelationType, List<RelatedItem>>(requiredItems);
|
||||
requiredItems.Clear();
|
||||
@@ -985,7 +996,7 @@ namespace Barotrauma.Items.Components
|
||||
#if CLIENT
|
||||
returnEmptyRequirements = Screen.Selected == GameMain.SubEditorScreen;
|
||||
#endif
|
||||
foreach (XElement subElement in element.Elements())
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
@@ -1016,8 +1027,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public virtual void ParseMsg()
|
||||
{
|
||||
string msg = TextManager.Get(Msg, true);
|
||||
if (msg != null)
|
||||
LocalizedString msg = TextManager.Get(Msg);
|
||||
if (msg.Loaded)
|
||||
{
|
||||
msg = TextManager.ParseInputTypes(msg);
|
||||
DisplayMsg = msg;
|
||||
@@ -1028,6 +1039,30 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public interface IEventData { }
|
||||
|
||||
public virtual bool ValidateEventData(NetEntityEvent.IData data)
|
||||
=> true;
|
||||
|
||||
protected T ExtractEventData<T>(NetEntityEvent.IData data) where T : IEventData
|
||||
=> TryExtractEventData(data, out T componentData)
|
||||
? componentData
|
||||
: throw new Exception($"Malformed item component state event for {item.Name} " +
|
||||
$"(item ID {item.ID}, component type {GetType().Name}): " +
|
||||
$"could not extract ComponentData of type {typeof(T).Name}");
|
||||
|
||||
protected bool TryExtractEventData<T>(NetEntityEvent.IData data, out T componentData)
|
||||
{
|
||||
componentData = default;
|
||||
if (data is Item.ComponentStateEventData { ComponentData: T nestedData })
|
||||
{
|
||||
componentData = nestedData;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#region AI related
|
||||
protected const float AIUpdateInterval = 0.2f;
|
||||
protected float aiUpdateTimer;
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
//how many items can be contained
|
||||
private int capacity;
|
||||
[Serialize(5, false, description: "How many items can be contained inside this item.")]
|
||||
[Serialize(5, IsPropertySaveable.No, description: "How many items can be contained inside this item.")]
|
||||
public int Capacity
|
||||
{
|
||||
get { return capacity; }
|
||||
@@ -66,7 +66,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
//how many items can be contained
|
||||
private int maxStackSize;
|
||||
[Serialize(64, false, description: "How many items can be stacked in one slot. Does not increase the maximum stack size of the items themselves, e.g. a stack of bullets could have a maximum size of 8 but the number of bullets in a specific weapon could be restricted to 6.")]
|
||||
[Serialize(64, IsPropertySaveable.No, description: "How many items can be stacked in one slot. Does not increase the maximum stack size of the items themselves, e.g. a stack of bullets could have a maximum size of 8 but the number of bullets in a specific weapon could be restricted to 6.")]
|
||||
public int MaxStackSize
|
||||
{
|
||||
get { return maxStackSize; }
|
||||
@@ -74,7 +74,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
private bool hideItems;
|
||||
[Serialize(true, false, description: "Should the items contained inside this item be hidden."
|
||||
[Serialize(true, IsPropertySaveable.No, description: "Should the items contained inside this item be hidden."
|
||||
+ " If set to false, you should use the ItemPos and ItemInterval properties to determine where the items get rendered.")]
|
||||
public bool HideItems
|
||||
{
|
||||
@@ -85,55 +85,55 @@ namespace Barotrauma.Items.Components
|
||||
Drawable = !hideItems;
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize("0.0,0.0", false, description: "The position where the contained items get drawn at (offset from the upper left corner of the sprite in pixels).")]
|
||||
|
||||
[Serialize("0.0,0.0", IsPropertySaveable.No, description: "The position where the contained items get drawn at (offset from the upper left corner of the sprite in pixels).")]
|
||||
public Vector2 ItemPos { get; set; }
|
||||
|
||||
[Serialize("0.0,0.0", false, description: "The interval at which the contained items are spaced apart from each other (in pixels).")]
|
||||
[Serialize("0.0,0.0", IsPropertySaveable.No, description: "The interval at which the contained items are spaced apart from each other (in pixels).")]
|
||||
public Vector2 ItemInterval { get; set; }
|
||||
|
||||
[Serialize(100, false, description: "How many items are placed in a row before starting a new row.")]
|
||||
[Serialize(100, IsPropertySaveable.No, description: "How many items are placed in a row before starting a new row.")]
|
||||
public int ItemsPerRow { get; set; }
|
||||
|
||||
[Serialize(true, false, description: "Should the inventory of this item be visible when the item is selected.")]
|
||||
[Serialize(true, IsPropertySaveable.No, description: "Should the inventory of this item be visible when the item is selected.")]
|
||||
public bool DrawInventory
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(true, false, "Allow dragging and dropping items to deposit items into this inventory.")]
|
||||
[Serialize(true, IsPropertySaveable.No, "Allow dragging and dropping items to deposit items into this inventory.")]
|
||||
public bool AllowDragAndDrop
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(true, false)]
|
||||
|
||||
[Serialize(true, IsPropertySaveable.No)]
|
||||
public bool AllowSwappingContainedItems
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "If set to true, interacting with this item will make the character interact with the contained item(s), automatically picking them up if they can be picked up.")]
|
||||
[Serialize(false, IsPropertySaveable.No, description: "If set to true, interacting with this item will make the character interact with the contained item(s), automatically picking them up if they can be picked up.")]
|
||||
public bool AutoInteractWithContained
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(true, false)]
|
||||
[Serialize(true, IsPropertySaveable.No)]
|
||||
public bool AllowAccess { get; set; }
|
||||
|
||||
[Serialize(false, false)]
|
||||
[Serialize(false, IsPropertySaveable.No)]
|
||||
public bool AccessOnlyWhenBroken { get; set; }
|
||||
|
||||
[Serialize(5, false, description: "How many inventory slots the inventory has per row.")]
|
||||
[Serialize(5, IsPropertySaveable.No, description: "How many inventory slots the inventory has per row.")]
|
||||
public int SlotsPerRow { get; set; }
|
||||
|
||||
private readonly HashSet<string> containableRestrictions = new HashSet<string>();
|
||||
[Editable, Serialize("", true, description: "Define items (by identifiers or tags) that bots should place inside this container. If empty, no restrictions are applied.")]
|
||||
[Editable, Serialize("", IsPropertySaveable.Yes, description: "Define items (by identifiers or tags) that bots should place inside this container. If empty, no restrictions are applied.")]
|
||||
public string ContainableRestrictions
|
||||
{
|
||||
get { return string.Join(",", containableRestrictions); }
|
||||
@@ -143,46 +143,46 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize(true, true, description: "Should this container be automatically filled with items?")]
|
||||
[Editable, Serialize(true, IsPropertySaveable.Yes, description: "Should this container be automatically filled with items?")]
|
||||
public bool AutoFill { get; set; }
|
||||
|
||||
private float itemRotation;
|
||||
[Serialize(0.0f, false, description: "The rotation in which the contained sprites are drawn (in degrees).")]
|
||||
[Serialize(0.0f, IsPropertySaveable.No, description: "The rotation in which the contained sprites are drawn (in degrees).")]
|
||||
public float ItemRotation
|
||||
{
|
||||
get { return MathHelper.ToDegrees(itemRotation); }
|
||||
set { itemRotation = MathHelper.ToRadians(value); }
|
||||
}
|
||||
|
||||
[Serialize("", false, description: "Specify an item for the container to spawn with.")]
|
||||
[Serialize("", IsPropertySaveable.No, description: "Specify an item for the container to spawn with.")]
|
||||
public string SpawnWithId
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "Should the items configured using SpawnWithId spawn if this item is broken.")]
|
||||
[Serialize(false, IsPropertySaveable.No, description: "Should the items configured using SpawnWithId spawn if this item is broken.")]
|
||||
public bool SpawnWithIdWhenBroken
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "Should the items be injected into the user.")]
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No, description: "Should the items be injected into the user.")]
|
||||
public bool AutoInject
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(0.5f, false, description: "The health threshold that the user must reach in order to activate the autoinjection.")]
|
||||
[Serialize(0.5f, IsPropertySaveable.No, description: "The health threshold that the user must reach in order to activate the autoinjection.")]
|
||||
public float AutoInjectThreshold
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, false)]
|
||||
[Serialize(false, IsPropertySaveable.No)]
|
||||
public bool RemoveContainedItemsOnDeconstruct { get; set; }
|
||||
|
||||
private SlotRestrictions[] slotRestrictions;
|
||||
@@ -202,20 +202,20 @@ namespace Barotrauma.Items.Components
|
||||
if (!isRestrictionsDefined) { return true; }
|
||||
return containableRestrictions.Any(id => item.Prefab.Identifier == id || item.HasTag(id));
|
||||
}
|
||||
|
||||
private ImmutableHashSet<string> containableItemIdentifiers;
|
||||
public IEnumerable<string> ContainableItemIdentifiers => containableItemIdentifiers;
|
||||
|
||||
private ImmutableHashSet<Identifier> containableItemIdentifiers;
|
||||
public IEnumerable<Identifier> ContainableItemIdentifiers => containableItemIdentifiers;
|
||||
|
||||
public override bool RecreateGUIOnResolutionChange => true;
|
||||
|
||||
public List<RelatedItem> ContainableItems { get; }
|
||||
|
||||
public ItemContainer(Item item, XElement element)
|
||||
public ItemContainer(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
int totalCapacity = capacity;
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
@@ -223,7 +223,7 @@ namespace Barotrauma.Items.Components
|
||||
RelatedItem containable = RelatedItem.Load(subElement, returnEmpty: false, parentDebugName: item.Name);
|
||||
if (containable == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in item config \"" + item.ConfigFile + "\" - containable with no identifiers.");
|
||||
DebugConsole.ThrowError("Error in item config \"" + item.ConfigFilePath + "\" - containable with no identifiers.");
|
||||
continue;
|
||||
}
|
||||
ContainableItems ??= new List<RelatedItem>();
|
||||
@@ -242,7 +242,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
int subContainerIndex = capacity;
|
||||
foreach (XElement subElement in element.Elements())
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
if (subElement.Name.ToString().ToLowerInvariant() != "subcontainer") { continue; }
|
||||
|
||||
@@ -250,14 +250,14 @@ namespace Barotrauma.Items.Components
|
||||
int subMaxStackSize = subElement.GetAttributeInt("maxstacksize", maxStackSize);
|
||||
|
||||
List<RelatedItem> subContainableItems = null;
|
||||
foreach (XElement subSubElement in subElement.Elements())
|
||||
foreach (var subSubElement in subElement.Elements())
|
||||
{
|
||||
if (subSubElement.Name.ToString().ToLowerInvariant() != "containable") { continue; }
|
||||
|
||||
RelatedItem containable = RelatedItem.Load(subSubElement, returnEmpty: false, parentDebugName: item.Name);
|
||||
if (containable == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in item config \"" + item.ConfigFile + "\" - containable with no identifiers.");
|
||||
DebugConsole.ThrowError("Error in item config \"" + item.ConfigFilePath + "\" - containable with no identifiers.");
|
||||
continue;
|
||||
}
|
||||
subContainableItems ??= new List<RelatedItem>();
|
||||
@@ -283,7 +283,7 @@ namespace Barotrauma.Items.Components
|
||||
return slotRestrictions[slotIndex].MaxStackSize;
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(XElement element);
|
||||
partial void InitProjSpecific(ContentXElement element);
|
||||
|
||||
public void OnItemContained(Item containedItem)
|
||||
{
|
||||
@@ -308,7 +308,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (item.GetComponent<Planter>() != null)
|
||||
{
|
||||
GameAnalyticsManager.AddDesignEvent("MicroInteraction:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "null") + ":GardeningPlanted:" + containedItem.prefab.Identifier);
|
||||
GameAnalyticsManager.AddDesignEvent("MicroInteraction:" + (GameMain.GameSession?.GameMode?.Preset.Identifier.Value ?? "null") + ":GardeningPlanted:" + containedItem.Prefab.Identifier);
|
||||
}
|
||||
|
||||
//no need to Update() if this item has no statuseffects and no physics body
|
||||
@@ -424,7 +424,16 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override bool HasRequiredItems(Character character, bool addMessage, string msg = null)
|
||||
public override void UpdateBroken(float deltaTime, Camera cam)
|
||||
{
|
||||
//update when the item is broken too to get OnContaining effects to execute and contained item positions to update
|
||||
if (IsActive)
|
||||
{
|
||||
Update(deltaTime, cam);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool HasRequiredItems(Character character, bool addMessage, LocalizedString msg = null)
|
||||
{
|
||||
return AllowAccess && (!AccessOnlyWhenBroken || Item.Condition <= 0) && base.HasRequiredItems(character, addMessage, msg);
|
||||
}
|
||||
@@ -648,7 +657,7 @@ namespace Barotrauma.Items.Components
|
||||
public override void OnItemLoaded()
|
||||
{
|
||||
Inventory.AllowSwappingContainedItems = AllowSwappingContainedItems;
|
||||
containableItemIdentifiers = slotRestrictions.SelectMany(s => s.ContainableItems?.SelectMany(ri => ri.Identifiers) ?? Enumerable.Empty<string>()).ToImmutableHashSet();
|
||||
containableItemIdentifiers = slotRestrictions.SelectMany(s => s.ContainableItems?.SelectMany(ri => ri.Identifiers) ?? Enumerable.Empty<Identifier>()).ToImmutableHashSet();
|
||||
if (item.Submarine == null || !item.Submarine.Loading)
|
||||
{
|
||||
SpawnAlwaysContainedItems();
|
||||
@@ -716,7 +725,7 @@ namespace Barotrauma.Items.Components
|
||||
else
|
||||
{
|
||||
IsActive = true;
|
||||
Entity.Spawner?.AddToSpawnQueue(prefab, Inventory, spawnIfInventoryFull: false, onSpawned: (Item item) => { alwaysContainedItemsSpawned = true; });
|
||||
Entity.Spawner?.AddItemToSpawnQueue(prefab, Inventory, spawnIfInventoryFull: false, onSpawned: (Item item) => { alwaysContainedItemsSpawned = true; });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -745,7 +754,7 @@ namespace Barotrauma.Items.Components
|
||||
Inventory.AllItemsMod.ForEach(it => it.Drop(null));
|
||||
}
|
||||
|
||||
public override void Load(XElement componentElement, bool usePrefabValues, IdRemap idRemap)
|
||||
public override void Load(ContentXElement componentElement, bool usePrefabValues, IdRemap idRemap)
|
||||
{
|
||||
base.Load(componentElement, usePrefabValues, idRemap);
|
||||
|
||||
|
||||
@@ -7,14 +7,14 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
public static List<Ladder> List { get; } = new List<Ladder>();
|
||||
|
||||
public Ladder(Item item, XElement element)
|
||||
public Ladder(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
InitProjSpecific(element);
|
||||
List.Add(this);
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(XElement element);
|
||||
partial void InitProjSpecific(ContentXElement element);
|
||||
|
||||
public override bool Select(Character character)
|
||||
{
|
||||
|
||||
@@ -5,7 +5,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -13,6 +12,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
[Editable]
|
||||
public LimbType LimbType { get; set; }
|
||||
|
||||
[Editable]
|
||||
public Vector2 Position { get; set; }
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public string Name => LimbType.ToString();
|
||||
|
||||
public Dictionary<string, SerializableProperty> SerializableProperties => null;
|
||||
public Dictionary<Identifier, SerializableProperty> SerializableProperties => null;
|
||||
|
||||
public LimbPos(LimbType limbType, Vector2 position, bool allowUsingLimb)
|
||||
{
|
||||
@@ -33,7 +33,7 @@ namespace Barotrauma.Items.Components
|
||||
partial class Controller : ItemComponent, IServerSerializable
|
||||
{
|
||||
//where the limbs of the user should be positioned when using the controller
|
||||
private readonly List<LimbPos> limbPositions;
|
||||
private readonly List<LimbPos> limbPositions = new List<LimbPos>();
|
||||
|
||||
private Direction dir;
|
||||
|
||||
@@ -61,21 +61,21 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public IEnumerable<LimbPos> LimbPositions { get { return limbPositions; } }
|
||||
|
||||
[Editable, Serialize(false, false, description: "When enabled, the item will continuously send out a 0/1 signal and interacting with it will flip the signal (making the item behave like a switch). When disabled, the item will simply send out 1 when interacted with.", alwaysUseInstanceValues: true)]
|
||||
[Editable, Serialize(false, IsPropertySaveable.No, description: "When enabled, the item will continuously send out a 0/1 signal and interacting with it will flip the signal (making the item behave like a switch). When disabled, the item will simply send out 1 when interacted with.", alwaysUseInstanceValues: true)]
|
||||
public bool IsToggle
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, false, description: "Whether the item is toggled on/off. Only valid if IsToggle is set to true.", alwaysUseInstanceValues: true)]
|
||||
[Editable, Serialize(false, IsPropertySaveable.No, description: "Whether the item is toggled on/off. Only valid if IsToggle is set to true.", alwaysUseInstanceValues: true)]
|
||||
public bool State
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(true, false, description: "Should the HUD (inventory, health bar, etc) be hidden when this item is selected.")]
|
||||
[Serialize(true, IsPropertySaveable.No, description: "Should the HUD (inventory, health bar, etc) be hidden when this item is selected.")]
|
||||
public bool HideHUD
|
||||
{
|
||||
get;
|
||||
@@ -87,10 +87,10 @@ namespace Barotrauma.Items.Components
|
||||
Air, Water, Both
|
||||
};
|
||||
|
||||
[Serialize(UseEnvironment.Both, false, description: "Can the item be selected in air, underwater or both.")]
|
||||
[Serialize(UseEnvironment.Both, IsPropertySaveable.No, description: "Can the item be selected in air, underwater or both.")]
|
||||
public UseEnvironment UsableIn { get; set; }
|
||||
|
||||
[Serialize(false, false, description: "Should the character using the item be drawn behind the item.")]
|
||||
[Serialize(false, IsPropertySaveable.No, description: "Should the character using the item be drawn behind the item.")]
|
||||
public bool DrawUserBehind
|
||||
{
|
||||
get;
|
||||
@@ -114,41 +114,12 @@ namespace Barotrauma.Items.Components
|
||||
private set;
|
||||
} = true;
|
||||
|
||||
public Controller(Item item, XElement element)
|
||||
public Controller(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
limbPositions = new List<LimbPos>();
|
||||
|
||||
userPos = element.GetAttributeVector2("UserPos", Vector2.Zero);
|
||||
|
||||
Enum.TryParse(element.GetAttributeString("direction", "None"), out dir);
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (subElement.Name != "limbposition") { continue; }
|
||||
string limbStr = subElement.GetAttributeString("limb", "");
|
||||
if (!Enum.TryParse(subElement.Attribute("limb").Value, out LimbType limbType))
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in item \"{item.Name}\" - {limbStr} is not a valid limb type.");
|
||||
}
|
||||
else
|
||||
{
|
||||
LimbPos limbPos = new LimbPos(limbType,
|
||||
subElement.GetAttributeVector2("position", Vector2.Zero),
|
||||
subElement.GetAttributeBool("allowusinglimb", false));
|
||||
limbPositions.Add(limbPos);
|
||||
if (!limbPos.AllowUsingLimb)
|
||||
{
|
||||
if (limbType == LimbType.RightHand || limbType == LimbType.RightForearm || limbType == LimbType.RightArm ||
|
||||
limbType == LimbType.LeftHand || limbType == LimbType.LeftForearm || limbType == LimbType.LeftArm)
|
||||
{
|
||||
AllowAiming = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LoadLimbPositions(element);
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
@@ -370,7 +341,10 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public Item GetFocusTarget()
|
||||
{
|
||||
item.SendSignal(new Signal(MathHelper.ToDegrees(targetRotation).ToString("G", CultureInfo.InvariantCulture), sender: user), "position_out");
|
||||
var positionOut = item.Connections?.Find(c => c.Name == "position_out");
|
||||
if (positionOut == null) { return null; }
|
||||
|
||||
item.SendSignal(new Signal(MathHelper.ToDegrees(targetRotation).ToString("G", CultureInfo.InvariantCulture), sender: user), positionOut);
|
||||
|
||||
for (int i = item.LastSentSignalRecipients.Count - 1; i >= 0; i--)
|
||||
{
|
||||
@@ -380,7 +354,16 @@ namespace Barotrauma.Items.Components
|
||||
return item.LastSentSignalRecipients[i].Item;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach (var recipientPanel in item.GetConnectedComponentsRecursive<ConnectionPanel>(positionOut, allowTraversingBackwards: false))
|
||||
{
|
||||
if (recipientPanel.Item.Condition <= 0.0f) { continue; }
|
||||
if (recipientPanel.Item.Prefab.FocusOnSelected)
|
||||
{
|
||||
return recipientPanel.Item;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -517,5 +500,63 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
partial void HideHUDs(bool value);
|
||||
|
||||
public override XElement Save(XElement parentElement)
|
||||
{
|
||||
return SaveLimbPositions(base.Save(parentElement));
|
||||
}
|
||||
|
||||
public override void Load(ContentXElement componentElement, bool usePrefabValues, IdRemap idRemap)
|
||||
{
|
||||
base.Load(componentElement, usePrefabValues, idRemap);
|
||||
if (GameMain.GameSession?.GameMode?.Preset == GameModePreset.TestMode)
|
||||
{
|
||||
LoadLimbPositions(componentElement);
|
||||
}
|
||||
}
|
||||
|
||||
private XElement SaveLimbPositions(XElement element)
|
||||
{
|
||||
if (Screen.Selected == GameMain.SubEditorScreen)
|
||||
{
|
||||
foreach (var limbPos in limbPositions)
|
||||
{
|
||||
element.Add(new XElement("limbposition",
|
||||
new XAttribute("limb", limbPos.LimbType),
|
||||
new XAttribute("position", XMLExtensions.Vector2ToString(limbPos.Position)),
|
||||
new XAttribute("allowusinglimb", limbPos.AllowUsingLimb)));
|
||||
}
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
private void LoadLimbPositions(XElement element)
|
||||
{
|
||||
limbPositions.Clear();
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
if (subElement.Name != "limbposition") { continue; }
|
||||
string limbStr = subElement.GetAttributeString("limb", "");
|
||||
if (!Enum.TryParse(subElement.GetAttribute("limb").Value, out LimbType limbType))
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in item \"{item.Name}\" - {limbStr} is not a valid limb type.");
|
||||
}
|
||||
else
|
||||
{
|
||||
LimbPos limbPos = new LimbPos(limbType,
|
||||
subElement.GetAttributeVector2("position", Vector2.Zero),
|
||||
subElement.GetAttributeBool("allowusinglimb", false));
|
||||
limbPositions.Add(limbPos);
|
||||
if (!limbPos.AllowUsingLimb)
|
||||
{
|
||||
if (limbType == LimbType.RightHand || limbType == LimbType.RightForearm || limbType == LimbType.RightArm ||
|
||||
limbType == LimbType.LeftHand || limbType == LimbType.LeftForearm || limbType == LimbType.LeftArm)
|
||||
{
|
||||
AllowAiming = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+20
-21
@@ -33,15 +33,15 @@ namespace Barotrauma.Items.Components
|
||||
get { return outputContainer; }
|
||||
}
|
||||
|
||||
[Serialize(false, true)]
|
||||
[Serialize(false, IsPropertySaveable.Yes)]
|
||||
public bool DeconstructItemsSimultaneously { get; set; }
|
||||
|
||||
[Editable, Serialize(1.0f, true)]
|
||||
[Editable, Serialize(1.0f, IsPropertySaveable.Yes)]
|
||||
public float DeconstructionSpeed { get; set; }
|
||||
|
||||
public override bool RecreateGUIOnResolutionChange => true;
|
||||
|
||||
public Deconstructor(Item item, XElement element)
|
||||
public Deconstructor(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
InitProjSpecific(element);
|
||||
@@ -88,8 +88,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
|
||||
if (powerConsumption <= 0.0f) { Voltage = 1.0f; }
|
||||
progressTimer += deltaTime * Math.Min(Voltage, 1.0f);
|
||||
progressTimer += deltaTime * Math.Min(powerConsumption <= 0.0f ? 1 : Voltage, 1.0f);
|
||||
|
||||
float tinkeringStrength = 0f;
|
||||
if (repairable.IsTinkering)
|
||||
@@ -114,9 +113,9 @@ namespace Barotrauma.Items.Components
|
||||
foreach (Item targetItem in items)
|
||||
{
|
||||
if ((Entity.Spawner?.IsInRemoveQueue(targetItem) ?? false) || !inputContainer.Inventory.AllItems.Contains(targetItem)) { continue; }
|
||||
var validDeconstructItems = targetItem.Prefab.DeconstructItems.FindAll(it =>
|
||||
(it.RequiredDeconstructor.Length == 0 || it.RequiredDeconstructor.Any(r => item.HasTag(r) || item.Prefab.Identifier.Equals(r, StringComparison.OrdinalIgnoreCase))) &&
|
||||
(it.RequiredOtherItem.Length == 0 || it.RequiredOtherItem.Any(r => items.Any(it => it != targetItem && (it.HasTag(r) || it.Prefab.Identifier.Equals(r, StringComparison.OrdinalIgnoreCase))))));
|
||||
var validDeconstructItems = targetItem.Prefab.DeconstructItems.Where(it =>
|
||||
(it.RequiredDeconstructor.Length == 0 || it.RequiredDeconstructor.Any(r => item.HasTag(r) || item.Prefab.Identifier == r)) &&
|
||||
(it.RequiredOtherItem.Length == 0 || it.RequiredOtherItem.Any(r => items.Any(it => it != targetItem && (it.HasTag(r) || it.Prefab.Identifier == r))))).ToList();
|
||||
|
||||
ProcessItem(targetItem, items, validDeconstructItems, allowRemove: validDeconstructItems.Any() || !targetItem.Prefab.DeconstructItems.Any());
|
||||
}
|
||||
@@ -133,8 +132,8 @@ namespace Barotrauma.Items.Components
|
||||
var targetItem = inputContainer.Inventory.LastOrDefault();
|
||||
if (targetItem == null) { return; }
|
||||
|
||||
var validDeconstructItems = targetItem.Prefab.DeconstructItems.FindAll(it =>
|
||||
it.RequiredDeconstructor.Length == 0 || it.RequiredDeconstructor.Any(r => item.HasTag(r) || item.Prefab.Identifier.Equals(r, StringComparison.OrdinalIgnoreCase)));
|
||||
var validDeconstructItems = targetItem.Prefab.DeconstructItems.Where(it =>
|
||||
it.RequiredDeconstructor.Length == 0 || it.RequiredDeconstructor.Any(r => item.HasTag(r) || item.Prefab.Identifier == r)).ToList();
|
||||
|
||||
float deconstructTime = validDeconstructItems.Any() ? targetItem.Prefab.DeconstructTime / (DeconstructionSpeed * deconstructionSpeedModifier) : 1.0f;
|
||||
|
||||
@@ -222,7 +221,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
float condition = deconstructProduct.CopyCondition ?
|
||||
percentageHealth * itemPrefab.Health :
|
||||
percentageHealth * itemPrefab.Health * deconstructProduct.OutConditionMax :
|
||||
itemPrefab.Health * Rand.Range(deconstructProduct.OutConditionMin, deconstructProduct.OutConditionMax);
|
||||
|
||||
if (DeconstructItemsSimultaneously && deconstructProduct.RequiredOtherItem.Length > 0)
|
||||
@@ -230,7 +229,7 @@ namespace Barotrauma.Items.Components
|
||||
foreach (Item otherItem in inputItems)
|
||||
{
|
||||
if (targetItem == otherItem) { continue; }
|
||||
if (deconstructProduct.RequiredOtherItem.Any(r => otherItem.HasTag(r) || r.Equals(otherItem.Prefab.Identifier, StringComparison.OrdinalIgnoreCase)))
|
||||
if (deconstructProduct.RequiredOtherItem.Any(r => otherItem.HasTag(r) || r == otherItem.Prefab.Identifier))
|
||||
{
|
||||
user?.CheckTalents(AbilityEffectType.OnGeneticMaterialCombinedOrRefined);
|
||||
foreach (Character character in Character.GetFriendlyCrew(user))
|
||||
@@ -246,14 +245,14 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
inputContainer.Inventory.RemoveItem(otherItem);
|
||||
OutputContainer.Inventory.RemoveItem(otherItem);
|
||||
Entity.Spawner.AddToRemoveQueue(otherItem);
|
||||
Entity.Spawner.AddItemToRemoveQueue(otherItem);
|
||||
}
|
||||
allowRemove = false;
|
||||
return;
|
||||
}
|
||||
inputContainer.Inventory.RemoveItem(otherItem);
|
||||
OutputContainer.Inventory.RemoveItem(otherItem);
|
||||
Entity.Spawner.AddToRemoveQueue(otherItem);
|
||||
Entity.Spawner.AddItemToRemoveQueue(otherItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -268,7 +267,7 @@ namespace Barotrauma.Items.Components
|
||||
int amount = (int)amountMultiplier;
|
||||
for (int i = 0; i < amount; i++)
|
||||
{
|
||||
Entity.Spawner.AddToSpawnQueue(itemPrefab, outputContainer.Inventory, condition, onSpawned: (Item spawnedItem) =>
|
||||
Entity.Spawner.AddItemToSpawnQueue(itemPrefab, outputContainer.Inventory, condition, onSpawned: (Item spawnedItem) =>
|
||||
{
|
||||
spawnedItem.StolenDuringRound = targetItem.StolenDuringRound;
|
||||
spawnedItem.AllowStealing = targetItem.AllowStealing;
|
||||
@@ -300,7 +299,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
GameAnalyticsManager.AddDesignEvent("ItemDeconstructed:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "none") + ":" + targetItem.prefab.Identifier);
|
||||
GameAnalyticsManager.AddDesignEvent("ItemDeconstructed:" + (GameMain.GameSession?.GameMode?.Preset.Identifier.Value ?? "none") + ":" + targetItem.Prefab.Identifier);
|
||||
|
||||
if (targetItem.AllowDeconstruct && allowRemove)
|
||||
{
|
||||
@@ -314,7 +313,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
inputContainer.Inventory.RemoveItem(targetItem);
|
||||
Entity.Spawner.AddToRemoveQueue(targetItem);
|
||||
Entity.Spawner.AddItemToRemoveQueue(targetItem);
|
||||
MoveInputQueue();
|
||||
PutItemsToLinkedContainer();
|
||||
}
|
||||
@@ -392,16 +391,16 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (deconstructItem.RequiredDeconstructor.Length > 0)
|
||||
{
|
||||
if (!deconstructItem.RequiredDeconstructor.Any(r => item.HasTag(r) || item.Prefab.Identifier.Equals(r, StringComparison.OrdinalIgnoreCase))) { continue; }
|
||||
if (!deconstructItem.RequiredDeconstructor.Any(r => item.HasTag(r) || item.Prefab.Identifier == r)) { continue; }
|
||||
}
|
||||
if (deconstructItem.RequiredOtherItem.Length > 0 && checkRequiredOtherItems)
|
||||
{
|
||||
if (!deconstructItem.RequiredOtherItem.Any(r => items.Any(it => it.HasTag(r) || it.Prefab.Identifier.Equals(r, StringComparison.OrdinalIgnoreCase)))) { continue; }
|
||||
if (!deconstructItem.RequiredOtherItem.Any(r => items.Any(it => it.HasTag(r) || it.Prefab.Identifier == r))) { continue; }
|
||||
bool validOtherItemFound = false;
|
||||
foreach (Item otherInputItem in items)
|
||||
{
|
||||
if (otherInputItem == inputItem) { continue; }
|
||||
if (!deconstructItem.RequiredOtherItem.Any(r => otherInputItem.HasTag(r) || otherInputItem.Prefab.Identifier.Equals(r, StringComparison.OrdinalIgnoreCase))) { continue; }
|
||||
if (!deconstructItem.RequiredOtherItem.Any(r => otherInputItem.HasTag(r) || otherInputItem.Prefab.Identifier == r)) { continue; }
|
||||
|
||||
var geneticMaterial1 = inputItem.GetComponent<GeneticMaterial>();
|
||||
var geneticMaterial2 = otherInputItem.GetComponent<GeneticMaterial>();
|
||||
@@ -427,7 +426,7 @@ namespace Barotrauma.Items.Components
|
||||
if (inputContainer.Inventory.IsEmpty()) { active = false; }
|
||||
|
||||
IsActive = active;
|
||||
currPowerConsumption = IsActive ? powerConsumption : 0.0f;
|
||||
//currPowerConsumption = IsActive ? powerConsumption : 0.0f;
|
||||
userDeconstructorSpeedMultiplier = user != null ? 1f + user.GetStatValue(StatTypes.DeconstructorSpeedMultiplier) : 1f;
|
||||
|
||||
#if SERVER
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Barotrauma.Items.Components
|
||||
public Character User;
|
||||
|
||||
[Editable(0.0f, 10000000.0f),
|
||||
Serialize(500.0f, true, description: "The amount of force exerted on the submarine when the engine is operating at full power.")]
|
||||
Serialize(500.0f, IsPropertySaveable.Yes, description: "The amount of force exerted on the submarine when the engine is operating at full power.")]
|
||||
public float MaxForce
|
||||
{
|
||||
get { return maxForce; }
|
||||
@@ -37,7 +37,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize("0.0,0.0", true,
|
||||
[Editable, Serialize("0.0,0.0", IsPropertySaveable.Yes,
|
||||
description: "The position of the propeller as an offset from the item's center (in pixels)."+
|
||||
" Determines where the particles spawn and the position that causes characters to take damage from the engine if the PropellerDamage is defined.")]
|
||||
public Vector2 PropellerPos
|
||||
@@ -46,7 +46,7 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, true)]
|
||||
[Editable, Serialize(false, IsPropertySaveable.Yes)]
|
||||
public bool DisablePropellerDamage
|
||||
{
|
||||
get;
|
||||
@@ -75,12 +75,12 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private const float TinkeringForceIncrease = 1.5f;
|
||||
|
||||
public Engine(Item item, XElement element)
|
||||
public Engine(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
@@ -93,7 +93,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)
|
||||
{
|
||||
@@ -103,14 +103,16 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
controlLockTimer -= deltaTime;
|
||||
|
||||
currPowerConsumption = Math.Abs(targetForce) / 100.0f * powerConsumption;
|
||||
//engines consume more power when in a bad condition
|
||||
item.GetComponent<Repairable>()?.AdjustPowerConsumption(ref currPowerConsumption);
|
||||
if (powerConsumption == 0.0f)
|
||||
{
|
||||
prevVoltage = 1;
|
||||
hasPower = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
hasPower = Voltage > MinVoltage;
|
||||
}
|
||||
|
||||
if (powerConsumption == 0.0f) { Voltage = 1.0f; }
|
||||
|
||||
prevVoltage = Voltage;
|
||||
hasPower = Voltage > MinVoltage;
|
||||
|
||||
Force = MathHelper.Lerp(force, (Voltage < MinVoltage) ? 0.0f : targetForce, deltaTime * 10.0f);
|
||||
if (Math.Abs(Force) > 1.0f)
|
||||
@@ -154,6 +156,33 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Power consumption of the engine. Only consume power when active and adjust consumption based on condition and target force.
|
||||
/// </summary>
|
||||
public override float GetCurrentPowerConsumption(Connection connection = null)
|
||||
{
|
||||
if (connection != this.powerIn)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
currPowerConsumption = Math.Abs(targetForce) / 100.0f * powerConsumption;
|
||||
//engines consume more power when in a bad condition
|
||||
item.GetComponent<Repairable>()?.AdjustPowerConsumption(ref currPowerConsumption);
|
||||
return currPowerConsumption;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When grid is resolved update the previous voltage
|
||||
/// </summary>
|
||||
public override void GridResolved(Connection connection)
|
||||
{
|
||||
if (connection == powerIn)
|
||||
{
|
||||
prevVoltage = Voltage;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateAITargets(float noise)
|
||||
{
|
||||
if (item.AiTarget != null)
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
@@ -11,7 +13,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class Fabricator : Powered, IServerSerializable, IClientSerializable
|
||||
{
|
||||
private readonly List<FabricationRecipe> fabricationRecipes = new List<FabricationRecipe>();
|
||||
private ImmutableDictionary<uint, FabricationRecipe> fabricationRecipes; //this is not readonly because tutorials fuck this up!!!!
|
||||
|
||||
private FabricationRecipe fabricatedItem;
|
||||
private float timeUntilReady;
|
||||
@@ -20,7 +22,7 @@ namespace Barotrauma.Items.Components
|
||||
private string savedFabricatedItem;
|
||||
private float savedTimeUntilReady, savedRequiredTime;
|
||||
|
||||
private readonly Dictionary<string, List<Item>> availableIngredients = new Dictionary<string, List<Item>>();
|
||||
private readonly Dictionary<Identifier, List<Item>> availableIngredients = new Dictionary<Identifier, List<Item>>();
|
||||
|
||||
const float RefreshIngredientsInterval = 1.0f;
|
||||
private float refreshIngredientsTimer;
|
||||
@@ -31,10 +33,10 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private ItemContainer inputContainer, outputContainer;
|
||||
|
||||
[Serialize(1.0f, true)]
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes)]
|
||||
public float FabricationSpeed { get; set; }
|
||||
|
||||
[Serialize(1.0f, true)]
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes)]
|
||||
public float SkillRequirementMultiplier { get; set; }
|
||||
|
||||
private const float TinkeringSpeedIncrease = 2.5f;
|
||||
@@ -78,10 +80,12 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private float progressState;
|
||||
|
||||
public Fabricator(Item item, XElement element)
|
||||
private readonly Dictionary<uint, int> fabricationLimits = new Dictionary<uint, int>();
|
||||
|
||||
public Fabricator(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
foreach (XElement subElement in element.Elements())
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
if (subElement.Name.ToString().Equals("fabricableitem", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
@@ -90,30 +94,28 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
var fabricationRecipes = new Dictionary<uint, FabricationRecipe>();
|
||||
foreach (ItemPrefab itemPrefab in ItemPrefab.Prefabs)
|
||||
{
|
||||
foreach (FabricationRecipe recipe in itemPrefab.FabricationRecipes)
|
||||
foreach (FabricationRecipe recipe in itemPrefab.FabricationRecipes.Values)
|
||||
{
|
||||
if (recipe.SuitableFabricatorIdentifiers.Length > 0)
|
||||
{
|
||||
if (!recipe.SuitableFabricatorIdentifiers.Any(i => item.prefab.Identifier == i || item.HasTag(i)))
|
||||
if (!recipe.SuitableFabricatorIdentifiers.Any(i => item.Prefab.Identifier == i || item.HasTag(i)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
fabricationRecipes.Add(recipe);
|
||||
fabricationRecipes.Add(recipe.RecipeHash, recipe);
|
||||
if (recipe.FabricationLimitMax >= 0)
|
||||
{
|
||||
fabricationLimits.Add(recipe.RecipeHash, Rand.Range(recipe.FabricationLimitMin, recipe.FabricationLimitMax + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
fabricationRecipes.Sort((r1, r2) =>
|
||||
{
|
||||
int hash1 = (int)r1.TargetItem.UIntIdentifier;
|
||||
int hash2 = (int)r2.TargetItem.UIntIdentifier;
|
||||
return hash1 - hash2;
|
||||
});
|
||||
this.fabricationRecipes = fabricationRecipes.ToImmutableDictionary();
|
||||
|
||||
state = FabricatorState.Stopped;
|
||||
|
||||
InitProjSpecific();
|
||||
}
|
||||
|
||||
public override void OnItemLoaded()
|
||||
@@ -129,9 +131,9 @@ namespace Barotrauma.Items.Components
|
||||
inputContainer = containers[0];
|
||||
outputContainer = containers[1];
|
||||
|
||||
foreach (var recipe in fabricationRecipes)
|
||||
foreach (var recipe in fabricationRecipes.Values)
|
||||
{
|
||||
if (recipe.RequiredItems.Count > inputContainer.Capacity)
|
||||
if (recipe.RequiredItems.Length > inputContainer.Capacity)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in item \"" + item.Name + "\": There's not enough room in the input inventory for the ingredients of \"" + recipe.TargetItem.Name + "\"!");
|
||||
}
|
||||
@@ -142,9 +144,6 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
partial void OnItemLoadedProjSpecific();
|
||||
|
||||
|
||||
partial void InitProjSpecific();
|
||||
|
||||
public override bool Select(Character character)
|
||||
{
|
||||
SelectProjSpecific(character);
|
||||
@@ -158,16 +157,11 @@ namespace Barotrauma.Items.Components
|
||||
return picker != null;
|
||||
}
|
||||
|
||||
public void RemoveFabricationRecipes(List<string> allowedIdentifiers)
|
||||
public void RemoveFabricationRecipes(IEnumerable<Identifier> allowedIdentifiers)
|
||||
{
|
||||
for (int i = 0; i < fabricationRecipes.Count; i++)
|
||||
{
|
||||
if (!allowedIdentifiers.Contains(fabricationRecipes[i].TargetItem.Identifier))
|
||||
{
|
||||
fabricationRecipes.RemoveAt(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
fabricationRecipes = fabricationRecipes
|
||||
.Where(kvp => allowedIdentifiers.Contains(kvp.Value.TargetItemPrefabIdentifier))
|
||||
.ToImmutableDictionary();
|
||||
|
||||
CreateRecipes();
|
||||
}
|
||||
@@ -193,17 +187,25 @@ namespace Barotrauma.Items.Components
|
||||
if (!isClient)
|
||||
{
|
||||
MoveIngredientsToInputContainer(selectedItem);
|
||||
if (selectedItem.RequiredMoney > 0 && CanBeFabricated(fabricatedItem, availableIngredients, user))
|
||||
{
|
||||
if (GameMain.GameSession?.GameMode is MultiPlayerCampaign)
|
||||
{
|
||||
user.Wallet.Deduct(selectedItem.RequiredMoney);
|
||||
}
|
||||
else if (GameMain.GameSession?.GameMode is CampaignMode campaign)
|
||||
{
|
||||
campaign.Bank.Deduct(selectedItem.RequiredMoney);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
requiredTime = GetRequiredTime(fabricatedItem, user);
|
||||
timeUntilReady = requiredTime;
|
||||
|
||||
inputContainer.Inventory.Locked = true;
|
||||
outputContainer.Inventory.Locked = true;
|
||||
|
||||
currPowerConsumption = powerConsumption;
|
||||
item.GetComponent<Repairable>()?.AdjustPowerConsumption(ref currPowerConsumption);
|
||||
|
||||
if (GameMain.NetworkMember?.IsServer ?? true)
|
||||
{
|
||||
State = FabricatorState.Active;
|
||||
@@ -211,7 +213,7 @@ namespace Barotrauma.Items.Components
|
||||
#if SERVER
|
||||
if (user != null && addToServerLog)
|
||||
{
|
||||
GameServer.Log(GameServer.CharacterLogName(user) + " started fabricating " + selectedItem.DisplayName + " in " + item.Name, ServerLog.MessageType.ItemInteraction);
|
||||
GameServer.Log(GameServer.CharacterLogName(user) + " started fabricating " + selectedItem.DisplayName.Value + " in " + item.Name, ServerLog.MessageType.ItemInteraction);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -237,13 +239,13 @@ namespace Barotrauma.Items.Components
|
||||
#if SERVER
|
||||
if (user != null)
|
||||
{
|
||||
GameServer.Log(GameServer.CharacterLogName(user) + " cancelled the fabrication of " + fabricatedItem.DisplayName + " in " + item.Name, ServerLog.MessageType.ItemInteraction);
|
||||
GameServer.Log(GameServer.CharacterLogName(user) + " cancelled the fabrication of " + fabricatedItem.DisplayName.Value + " in " + item.Name, ServerLog.MessageType.ItemInteraction);
|
||||
}
|
||||
#elif CLIENT
|
||||
itemList.Enabled = true;
|
||||
if (activateButton != null)
|
||||
{
|
||||
activateButton.Text = TextManager.Get("FabricatorCreate");
|
||||
activateButton.Text = TextManager.Get(CreateButtonText);
|
||||
}
|
||||
#endif
|
||||
fabricatedItem = null;
|
||||
@@ -304,11 +306,10 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
|
||||
if (powerConsumption <= 0) { Voltage = 1.0f; }
|
||||
|
||||
float fabricationSpeedIncrease = 1f + tinkeringStrength * TinkeringSpeedIncrease;
|
||||
|
||||
timeUntilReady -= deltaTime * fabricationSpeedIncrease * Math.Min(Voltage, 1.0f);
|
||||
timeUntilReady -= deltaTime * fabricationSpeedIncrease * Math.Min(powerConsumption <= 0 ? 1 : Voltage, 1.0f);
|
||||
|
||||
UpdateRequiredTimeProjSpecific();
|
||||
|
||||
@@ -365,12 +366,19 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
foreach (Item containedItem in availableItem.OwnInventory.AllItemsMod)
|
||||
{
|
||||
containedItem.Drop(dropper: null);
|
||||
if (availableItem.GetComponent<ItemContainer>()?.RemoveContainedItemsOnDeconstruct ?? false)
|
||||
{
|
||||
Entity.Spawner.AddItemToRemoveQueue(containedItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
containedItem.Drop(dropper: null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
availableItems.Remove(availableItem);
|
||||
Entity.Spawner.AddToRemoveQueue(availableItem);
|
||||
Entity.Spawner.AddItemToRemoveQueue(availableItem);
|
||||
inputContainer.Inventory.RemoveItem(availableItem);
|
||||
break;
|
||||
}
|
||||
@@ -382,25 +390,42 @@ namespace Barotrauma.Items.Components
|
||||
var fabricationitemAmount = new AbilityFabricationItemAmount(fabricatedItem.TargetItem, fabricatedItem.Amount);
|
||||
|
||||
int quality = 0;
|
||||
if (user?.Info != null)
|
||||
if (fabricatedItem.Quality.HasValue)
|
||||
{
|
||||
quality = fabricatedItem.Quality.Value;
|
||||
}
|
||||
else if (user?.Info != null)
|
||||
{
|
||||
foreach (Character character in Character.GetFriendlyCrew(user))
|
||||
{
|
||||
character.CheckTalents(AbilityEffectType.OnAllyItemFabricatedAmount, fabricationitemAmount);
|
||||
}
|
||||
user.CheckTalents(AbilityEffectType.OnItemFabricatedAmount, fabricationitemAmount);
|
||||
|
||||
user.CheckTalents(AbilityEffectType.OnItemFabricatedAmount, fabricationitemAmount);
|
||||
quality = GetFabricatedItemQuality(fabricatedItem, user);
|
||||
}
|
||||
|
||||
int amount = (int)fabricationitemAmount.Value;
|
||||
if (fabricationLimits.ContainsKey(fabricatedItem.RecipeHash))
|
||||
{
|
||||
if (amount > fabricationLimits[fabricatedItem.RecipeHash])
|
||||
{
|
||||
amount = fabricationLimits[fabricatedItem.RecipeHash];
|
||||
fabricationLimits[fabricatedItem.RecipeHash] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
fabricationLimits[fabricatedItem.RecipeHash] -= amount;
|
||||
}
|
||||
}
|
||||
|
||||
var tempUser = user;
|
||||
for (int i = 0; i < (int)fabricationitemAmount.Value; i++)
|
||||
for (int i = 0; i < amount; i++)
|
||||
{
|
||||
float outCondition = fabricatedItem.OutCondition;
|
||||
GameAnalyticsManager.AddDesignEvent("ItemFabricated:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "none") + ":" + fabricatedItem.TargetItem.Identifier);
|
||||
GameAnalyticsManager.AddDesignEvent("ItemFabricated:" + (GameMain.GameSession?.GameMode?.Preset.Identifier.Value ?? "none") + ":" + fabricatedItem.TargetItem.Identifier);
|
||||
if (i < amountFittingContainer)
|
||||
{
|
||||
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, outputContainer.Inventory, fabricatedItem.TargetItem.Health * outCondition, quality,
|
||||
Entity.Spawner.AddItemToSpawnQueue(fabricatedItem.TargetItem, outputContainer.Inventory, fabricatedItem.TargetItem.Health * outCondition, quality,
|
||||
onSpawned: (Item spawnedItem) =>
|
||||
{
|
||||
onItemSpawned(spawnedItem, tempUser);
|
||||
@@ -413,7 +438,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, item.Position, item.Submarine, fabricatedItem.TargetItem.Health * outCondition, quality,
|
||||
Entity.Spawner.AddItemToSpawnQueue(fabricatedItem.TargetItem, item.Position, item.Submarine, fabricatedItem.TargetItem.Health * outCondition, quality,
|
||||
onSpawned: (Item spawnedItem) =>
|
||||
{
|
||||
onItemSpawned(spawnedItem, tempUser);
|
||||
@@ -469,13 +494,30 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Power consumption of the fabricator. Only consume power when active and adjust consumption based on condition.
|
||||
/// </summary>
|
||||
public override float GetCurrentPowerConsumption(Connection connection = null)
|
||||
{
|
||||
//No consumption if not powerin or is off
|
||||
if (connection != this.powerIn || !IsActive)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
currPowerConsumption = PowerConsumption;
|
||||
item.GetComponent<Repairable>()?.AdjustPowerConsumption(ref currPowerConsumption);
|
||||
|
||||
return currPowerConsumption;
|
||||
}
|
||||
|
||||
private int GetFabricatedItemQuality(FabricationRecipe fabricatedItem, Character user)
|
||||
{
|
||||
if (user == null) { return 0; }
|
||||
if (fabricatedItem.TargetItem.ConfigElement.GetChildElement("Quality") == null) { return 0; }
|
||||
int quality = 0;
|
||||
float floatQuality = 0.0f;
|
||||
foreach (string tag in fabricatedItem.TargetItem.Tags)
|
||||
foreach (var tag in fabricatedItem.TargetItem.Tags)
|
||||
{
|
||||
floatQuality += user.Info.GetSavedStatValue(StatTypes.IncreaseFabricationQuality, tag);
|
||||
}
|
||||
@@ -489,12 +531,33 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
partial void UpdateRequiredTimeProjSpecific();
|
||||
|
||||
private bool CanBeFabricated(FabricationRecipe fabricableItem, Dictionary<string, List<Item>> availableIngredients, Character character)
|
||||
|
||||
private bool CanBeFabricated(FabricationRecipe fabricableItem, IReadOnlyDictionary<Identifier, List<Item>> availableIngredients, Character character)
|
||||
{
|
||||
if (fabricableItem == null) { return false; }
|
||||
if (fabricableItem.RequiresRecipe && (character == null || !character.HasRecipeForItem(fabricableItem.TargetItem.Identifier))) { return false; }
|
||||
|
||||
if (fabricableItem.RequiredMoney > 0)
|
||||
{
|
||||
if (GameMain.GameSession?.GameMode is MultiPlayerCampaign)
|
||||
{
|
||||
if (character?.Wallet == null || character.Wallet.Balance < fabricableItem.RequiredMoney) { return false; }
|
||||
}
|
||||
else if (GameMain.GameSession?.GameMode is CampaignMode campaign)
|
||||
{
|
||||
if (campaign.Bank.Balance < fabricableItem.RequiredMoney) { return false; }
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (fabricationLimits.TryGetValue(fabricableItem.RecipeHash, out int amount) && amount <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return fabricableItem.RequiredItems.All(requiredItem =>
|
||||
{
|
||||
int availablePrefabsAmount = 0;
|
||||
@@ -533,13 +596,13 @@ namespace Barotrauma.Items.Components
|
||||
return fabricableItem.RequiredTime / FabricationSpeed / MathHelper.Clamp(t, 0.01f, 2.0f);
|
||||
}
|
||||
|
||||
public float FabricationDegreeOfSuccess(Character character, List<Skill> skills)
|
||||
public float FabricationDegreeOfSuccess(Character character, ImmutableArray<Skill> skills)
|
||||
{
|
||||
if (skills.Count == 0) { return 1.0f; }
|
||||
if (skills.Length == 0) { return 1.0f; }
|
||||
if (character == null) { return 0.0f; }
|
||||
|
||||
float skillSum = (from t in skills let characterLevel = character.GetSkillLevel(t.Identifier) select (characterLevel - (t.Level * SkillRequirementMultiplier))).Sum();
|
||||
float average = skillSum / skills.Count;
|
||||
float average = skillSum / skills.Length;
|
||||
|
||||
return (average + 100.0f) / 2.0f / 100.0f;
|
||||
}
|
||||
@@ -593,7 +656,7 @@ namespace Barotrauma.Items.Components
|
||||
availableIngredients.Clear();
|
||||
foreach (Item item in itemList)
|
||||
{
|
||||
var itemIdentifier = item.prefab.Identifier;
|
||||
var itemIdentifier = item.Prefab.Identifier;
|
||||
if (!availableIngredients.ContainsKey(itemIdentifier))
|
||||
{
|
||||
availableIngredients[itemIdentifier] = new List<Item>(itemList.Count);
|
||||
@@ -658,12 +721,11 @@ namespace Barotrauma.Items.Components
|
||||
componentElement.Add(new XAttribute("fabricateditemidentifier", fabricatedItem.TargetItem.Identifier));
|
||||
componentElement.Add(new XAttribute("savedtimeuntilready", timeUntilReady.ToString("G", CultureInfo.InvariantCulture)));
|
||||
componentElement.Add(new XAttribute("savedrequiredtime", requiredTime.ToString("G", CultureInfo.InvariantCulture)));
|
||||
|
||||
}
|
||||
return componentElement;
|
||||
}
|
||||
|
||||
public override void Load(XElement componentElement, bool usePrefabValues, IdRemap idRemap)
|
||||
public override void Load(ContentXElement componentElement, bool usePrefabValues, IdRemap idRemap)
|
||||
{
|
||||
base.Load(componentElement, usePrefabValues, idRemap);
|
||||
savedFabricatedItem = componentElement.GetAttributeString("fabricateditemidentifier", "");
|
||||
@@ -678,7 +740,7 @@ namespace Barotrauma.Items.Components
|
||||
inputContainer?.OnMapLoaded();
|
||||
outputContainer?.OnMapLoaded();
|
||||
|
||||
var recipe = fabricationRecipes.Find(r => r.TargetItem.Identifier == savedFabricatedItem);
|
||||
var recipe = fabricationRecipes.Values.FirstOrDefault(r => r.TargetItem.Identifier == savedFabricatedItem);
|
||||
if (recipe == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error while loading a fabricator. Can't continue fabricating \"" + savedFabricatedItem + "\" (matching recipe not found).");
|
||||
@@ -696,13 +758,13 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
class AbilityFabricatorSkillGain : AbilityObject, IAbilityValue, IAbilitySkillIdentifier
|
||||
{
|
||||
public AbilityFabricatorSkillGain(string skillIdentifier, float skillAmount)
|
||||
public AbilityFabricatorSkillGain(Identifier skillIdentifier, float skillAmount)
|
||||
{
|
||||
SkillIdentifier = skillIdentifier;
|
||||
Value = skillAmount;
|
||||
}
|
||||
public float Value { get; set; }
|
||||
public string SkillIdentifier { get; set; }
|
||||
public Identifier SkillIdentifier { get; set; }
|
||||
}
|
||||
|
||||
class AbilityFabricationItemAmount : AbilityObject, IAbilityValue, IAbilityItemPrefab
|
||||
|
||||
@@ -29,49 +29,49 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private readonly Dictionary<Hull, HullData> hullDatas;
|
||||
|
||||
[Editable, Serialize(false, true, description: "Does the machine require inputs from water detectors in order to show the water levels inside rooms.")]
|
||||
[Editable, Serialize(false, IsPropertySaveable.Yes, description: "Does the machine require inputs from water detectors in order to show the water levels inside rooms.")]
|
||||
public bool RequireWaterDetectors
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(true, true, description: "Does the machine require inputs from oxygen detectors in order to show the oxygen levels inside rooms.")]
|
||||
[Editable, Serialize(true, IsPropertySaveable.Yes, description: "Does the machine require inputs from oxygen detectors in order to show the oxygen levels inside rooms.")]
|
||||
public bool RequireOxygenDetectors
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(true, true, description: "Should damaged walls be displayed by the machine.")]
|
||||
[Editable, Serialize(true, IsPropertySaveable.Yes, description: "Should damaged walls be displayed by the machine.")]
|
||||
public bool ShowHullIntegrity
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(true, true, description: "Enable hull status mode.")]
|
||||
[Editable, Serialize(true, IsPropertySaveable.Yes, description: "Enable hull status mode.")]
|
||||
public bool EnableHullStatus
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(true, true, description: "Enable electrical view mode.")]
|
||||
[Editable, Serialize(true, IsPropertySaveable.Yes, description: "Enable electrical view mode.")]
|
||||
public bool EnableElectricalView
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(true, true, description: "Enable item finder mode.")]
|
||||
[Editable, Serialize(true, IsPropertySaveable.Yes, description: "Enable item finder mode.")]
|
||||
public bool EnableItemFinder
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public MiniMap(Item item, XElement element)
|
||||
public MiniMap(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
@@ -114,9 +114,6 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
#endif
|
||||
|
||||
currPowerConsumption = powerConsumption;
|
||||
currPowerConsumption *= MathHelper.Lerp(1.5f, 1.0f, item.Condition / item.MaxCondition);
|
||||
|
||||
hasPower = Voltage > MinVoltage;
|
||||
if (hasPower)
|
||||
{
|
||||
@@ -124,6 +121,19 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Power consumption of the MiniMap. Only consume power when active and adjust consumption based on condition.
|
||||
/// </summary>
|
||||
public override float GetCurrentPowerConsumption(Connection connection = null)
|
||||
{
|
||||
if (connection != powerIn || !IsActive)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return PowerConsumption * MathHelper.Lerp(1.5f, 1.0f, item.Condition / item.MaxCondition);
|
||||
}
|
||||
|
||||
public override bool Pick(Character picker)
|
||||
{
|
||||
return picker != null;
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class OutpostTerminal : ItemComponent
|
||||
{
|
||||
public OutpostTerminal(Item item, XElement element) : base(item, element)
|
||||
public OutpostTerminal(Item item, ContentXElement element) : base(item, element)
|
||||
{
|
||||
InitProjSpecific(element);
|
||||
}
|
||||
|
||||
+22
-15
@@ -24,14 +24,14 @@ namespace Barotrauma.Items.Components
|
||||
private set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(400.0f, true, description: "How much oxygen the machine generates when operating at full power.", alwaysUseInstanceValues: true)]
|
||||
[Editable, Serialize(400.0f, IsPropertySaveable.Yes, description: "How much oxygen the machine generates when operating at full power.", alwaysUseInstanceValues: true)]
|
||||
public float GeneratedAmount
|
||||
{
|
||||
get { return generatedAmount; }
|
||||
set { generatedAmount = MathHelper.Clamp(value, -10000.0f, 10000.0f); }
|
||||
}
|
||||
|
||||
public OxygenGenerator(Item item, XElement element)
|
||||
public OxygenGenerator(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
//randomize update timer so all oxygen generators don't update at the same time
|
||||
@@ -44,25 +44,15 @@ namespace Barotrauma.Items.Components
|
||||
UpdateOnActiveEffects(deltaTime);
|
||||
|
||||
CurrFlow = 0.0f;
|
||||
currPowerConsumption = powerConsumption;
|
||||
//consume more power when in a bad condition
|
||||
item.GetComponent<Repairable>()?.AdjustPowerConsumption(ref currPowerConsumption);
|
||||
|
||||
if (powerConsumption <= 0.0f)
|
||||
{
|
||||
Voltage = 1.0f;
|
||||
}
|
||||
|
||||
if (item.CurrentHull == null) { return; }
|
||||
|
||||
if (Voltage < MinVoltage)
|
||||
|
||||
if (Voltage < MinVoltage && PowerConsumption > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CurrFlow = Math.Min(Voltage, 1.0f) * generatedAmount * 100.0f;
|
||||
|
||||
//less effective when in bad condition
|
||||
CurrFlow = Math.Min(PowerConsumption > 0 ? Voltage : 1.0f, 1.0f) * generatedAmount * 100.0f;
|
||||
float conditionMult = item.Condition / item.MaxCondition;
|
||||
//100% condition = 100% oxygen
|
||||
//50% condition = 25% oxygen
|
||||
@@ -72,6 +62,23 @@ namespace Barotrauma.Items.Components
|
||||
UpdateVents(CurrFlow, deltaTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Power consumption of the Oxygen Generator. Only consume power when active and adjust consumption based on condition.
|
||||
/// </summary>
|
||||
public override float GetCurrentPowerConsumption(Connection connection = null)
|
||||
{
|
||||
if (connection != this.powerIn)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
float consumption = powerConsumption;
|
||||
|
||||
//consume more power when in a bad condition
|
||||
item.GetComponent<Repairable>()?.AdjustPowerConsumption(ref consumption);
|
||||
return consumption;
|
||||
}
|
||||
|
||||
public override void UpdateBroken(float deltaTime, Camera cam)
|
||||
{
|
||||
base.UpdateBroken(deltaTime, cam);
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private float pumpSpeedLockTimer, isActiveLockTimer;
|
||||
|
||||
[Serialize(0.0f, true, description: "How fast the item is currently pumping water (-100 = full speed out, 100 = full speed in). Intended to be used by StatusEffect conditionals (setting this value in XML has no effect).")]
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes, description: "How fast the item is currently pumping water (-100 = full speed out, 100 = full speed in). Intended to be used by StatusEffect conditionals (setting this value in XML has no effect).")]
|
||||
public float FlowPercentage
|
||||
{
|
||||
get { return flowPercentage; }
|
||||
@@ -48,18 +48,18 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (!MathUtils.IsValid(flowPercentage)) { return; }
|
||||
flowPercentage = MathHelper.Clamp(value, -100.0f, 100.0f);
|
||||
flowPercentage = MathUtils.Round(flowPercentage, 1.0f);
|
||||
flowPercentage = MathF.Round(flowPercentage);
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize(80.0f, false, description: "How fast the item pumps water in/out when operating at 100%.", alwaysUseInstanceValues: true)]
|
||||
[Editable, Serialize(80.0f, IsPropertySaveable.No, description: "How fast the item pumps water in/out when operating at 100%.", alwaysUseInstanceValues: true)]
|
||||
public float MaxFlow
|
||||
{
|
||||
get { return maxFlow; }
|
||||
set { maxFlow = value; }
|
||||
}
|
||||
|
||||
[Editable, Serialize(true, true, alwaysUseInstanceValues: true)]
|
||||
[Editable, Serialize(true, IsPropertySaveable.Yes, alwaysUseInstanceValues: true)]
|
||||
public bool IsOn
|
||||
{
|
||||
get { return IsActive; }
|
||||
@@ -83,13 +83,13 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override bool UpdateWhenInactive => true;
|
||||
|
||||
public Pump(Item item, XElement element)
|
||||
public Pump(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
InitProjSpecific(element);
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(XElement element);
|
||||
partial void InitProjSpecific(ContentXElement element);
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
@@ -120,10 +120,6 @@ namespace Barotrauma.Items.Components
|
||||
FlowPercentage = ((float)TargetLevel - hullPercentage) * 10.0f;
|
||||
}
|
||||
|
||||
currPowerConsumption = powerConsumption * Math.Abs(flowPercentage / 100.0f);
|
||||
//pumps consume more power when in a bad condition
|
||||
item.GetComponent<Repairable>()?.AdjustPowerConsumption(ref currPowerConsumption);
|
||||
|
||||
if (!HasPower) { return; }
|
||||
|
||||
UpdateProjSpecific(deltaTime);
|
||||
@@ -147,10 +143,9 @@ namespace Barotrauma.Items.Components
|
||||
item.CurrentHull.WaterVolume += currFlow * deltaTime * Timing.FixedUpdateRate;
|
||||
if (item.CurrentHull.WaterVolume > item.CurrentHull.Volume) { item.CurrentHull.Pressure += 30.0f * deltaTime; }
|
||||
|
||||
Voltage -= deltaTime;
|
||||
}
|
||||
|
||||
public void InfectBallast(string identifier, bool allowMultiplePerShip = false)
|
||||
public void InfectBallast(Identifier identifier, bool allowMultiplePerShip = false)
|
||||
{
|
||||
Hull hull = item.CurrentHull;
|
||||
if (hull == null) { return; }
|
||||
@@ -158,7 +153,7 @@ namespace Barotrauma.Items.Components
|
||||
if (!allowMultiplePerShip)
|
||||
{
|
||||
// if the ship is already infected then do nothing
|
||||
if (Hull.hullList.Where(h => h.Submarine == hull.Submarine).Any(h => h.BallastFlora != null)) { return; }
|
||||
if (Hull.HullList.Where(h => h.Submarine == hull.Submarine).Any(h => h.BallastFlora != null)) { return; }
|
||||
}
|
||||
|
||||
if (hull.BallastFlora != null) { return; }
|
||||
@@ -174,10 +169,28 @@ namespace Barotrauma.Items.Components
|
||||
hull.BallastFlora = new BallastFloraBehavior(hull, ballastFloraPrefab, offset, firstGrowth: true);
|
||||
|
||||
#if SERVER
|
||||
hull.BallastFlora.SendNetworkMessage(hull.BallastFlora, BallastFloraBehavior.NetworkHeader.Spawn);
|
||||
hull.BallastFlora.CreateNetworkMessage(new BallastFloraBehavior.SpawnEventData());
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Power consumption of the Pump. Only consume power when active and adjust consumption based on condition.
|
||||
/// </summary>
|
||||
public override float GetCurrentPowerConsumption(Connection connection = null)
|
||||
{
|
||||
//There shouldn't be other power connections to this
|
||||
if (connection != this.powerIn)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
currPowerConsumption = powerConsumption * Math.Abs(flowPercentage / 100.0f);
|
||||
//pumps consume more power when in a bad condition
|
||||
item.GetComponent<Repairable>()?.AdjustPowerConsumption(ref currPowerConsumption);
|
||||
|
||||
return currPowerConsumption;
|
||||
}
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime);
|
||||
|
||||
public override void ReceiveSignal(Signal signal, Connection connection)
|
||||
@@ -218,7 +231,8 @@ namespace Barotrauma.Items.Components
|
||||
#if CLIENT
|
||||
if (GameMain.Client != null) { return false; }
|
||||
#endif
|
||||
switch (objective.Option.ToLowerInvariant())
|
||||
|
||||
switch (objective.Option.Value.ToLowerInvariant())
|
||||
{
|
||||
case "pumpout":
|
||||
#if SERVER
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -36,8 +34,8 @@ namespace Barotrauma.Items.Components
|
||||
private float fireTimer, fireDelay;
|
||||
|
||||
private float maxPowerOutput;
|
||||
|
||||
private readonly Queue<float> loadQueue = new Queue<float>();
|
||||
private float minUpdatePowerOut;
|
||||
private float maxUpdatePowerOut;
|
||||
|
||||
private bool unsentChanges;
|
||||
private float sendUpdateTimer;
|
||||
@@ -50,7 +48,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private bool _powerOn;
|
||||
|
||||
[Serialize(defaultValue: false, isSaveable: true)]
|
||||
[Serialize(defaultValue: false, isSaveable: IsPropertySaveable.Yes)]
|
||||
public bool PowerOn
|
||||
{
|
||||
get { return _powerOn; }
|
||||
@@ -63,9 +61,11 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
protected override PowerPriority Priority { get { return PowerPriority.Reactor; } }
|
||||
|
||||
public Character LastAIUser { get; private set; }
|
||||
|
||||
[Serialize(defaultValue: false, isSaveable: true)]
|
||||
[Serialize(defaultValue: false, isSaveable: IsPropertySaveable.Yes)]
|
||||
public bool LastUserWasPlayer { get; private set; }
|
||||
|
||||
private Character lastUser;
|
||||
@@ -81,7 +81,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Editable(0.0f, float.MaxValue), Serialize(10000.0f, true, description: "How much power (kW) the reactor generates when operating at full capacity.", alwaysUseInstanceValues: true)]
|
||||
[Editable(0.0f, float.MaxValue), Serialize(10000.0f, IsPropertySaveable.Yes, description: "How much power (kW) the reactor generates when operating at full capacity.", alwaysUseInstanceValues: true)]
|
||||
public float MaxPowerOutput
|
||||
{
|
||||
get { return maxPowerOutput; }
|
||||
@@ -91,21 +91,21 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Editable(0.0f, float.MaxValue), Serialize(120.0f, true, description: "How long the temperature has to stay critical until a meltdown occurs.")]
|
||||
[Editable(0.0f, float.MaxValue), Serialize(120.0f, IsPropertySaveable.Yes, description: "How long the temperature has to stay critical until a meltdown occurs.")]
|
||||
public float MeltdownDelay
|
||||
{
|
||||
get { return meltDownDelay; }
|
||||
set { meltDownDelay = Math.Max(value, 0.0f); }
|
||||
}
|
||||
|
||||
[Editable(0.0f, float.MaxValue), Serialize(30.0f, true, description: "How long the temperature has to stay critical until the reactor catches fire.")]
|
||||
[Editable(0.0f, float.MaxValue), Serialize(30.0f, IsPropertySaveable.Yes, description: "How long the temperature has to stay critical until the reactor catches fire.")]
|
||||
public float FireDelay
|
||||
{
|
||||
get { return fireDelay; }
|
||||
set { fireDelay = Math.Max(value, 0.0f); }
|
||||
}
|
||||
|
||||
[Serialize(0.0f, true, description: "Current temperature of the reactor (0% - 100%). Indended to be used by StatusEffect conditionals.")]
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes, description: "Current temperature of the reactor (0% - 100%). Indended to be used by StatusEffect conditionals.")]
|
||||
public float Temperature
|
||||
{
|
||||
get { return temperature; }
|
||||
@@ -116,7 +116,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(0.0f, true, description: "Current fission rate of the reactor (0% - 100%). Intended to be used by StatusEffect conditionals (setting the value from XML is not recommended).")]
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes, description: "Current fission rate of the reactor (0% - 100%). Intended to be used by StatusEffect conditionals (setting the value from XML is not recommended).")]
|
||||
public float FissionRate
|
||||
{
|
||||
get { return fissionRate; }
|
||||
@@ -127,7 +127,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(0.0f, true, description: "Current turbine output of the reactor (0% - 100%). Intended to be used by StatusEffect conditionals (setting the value from XML is not recommended).")]
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes, description: "Current turbine output of the reactor (0% - 100%). Intended to be used by StatusEffect conditionals (setting the value from XML is not recommended).")]
|
||||
public float TurbineOutput
|
||||
{
|
||||
get { return turbineOutput; }
|
||||
@@ -138,7 +138,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(0.2f, true, description: "How fast the condition of the contained fuel rods deteriorates per second."), Editable(0.0f, 1000.0f, decimals: 3)]
|
||||
[Serialize(0.2f, IsPropertySaveable.Yes, description: "How fast the condition of the contained fuel rods deteriorates per second."), Editable(0.0f, 1000.0f, decimals: 3)]
|
||||
public float FuelConsumptionRate
|
||||
{
|
||||
get { return fuelConsumptionRate; }
|
||||
@@ -149,14 +149,14 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(false, true, description: "Is the temperature currently critical. Intended to be used by StatusEffect conditionals (setting the value from XML has no effect).")]
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Is the temperature currently critical. Intended to be used by StatusEffect conditionals (setting the value from XML has no effect).")]
|
||||
public bool TemperatureCritical
|
||||
{
|
||||
get { return temperature > allowedTemperature.Y; }
|
||||
set { /*do nothing*/ }
|
||||
}
|
||||
|
||||
[Serialize(false, true, description: "Is the automatic temperature control currently on. Indended to be used by StatusEffect conditionals (setting the value from XML is not recommended).")]
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Is the automatic temperature control currently on. Indended to be used by StatusEffect conditionals (setting the value from XML is not recommended).")]
|
||||
public bool AutoTemp
|
||||
{
|
||||
get { return autoTemp; }
|
||||
@@ -171,36 +171,36 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private float prevAvailableFuel;
|
||||
|
||||
[Serialize(0.0f, true)]
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes)]
|
||||
public float AvailableFuel { get; set; }
|
||||
|
||||
[Serialize(0.0f, true)]
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes)]
|
||||
public new float Load { get; private set; }
|
||||
|
||||
[Serialize(0.0f, true)]
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes)]
|
||||
public float TargetFissionRate { get; set; }
|
||||
|
||||
[Serialize(0.0f, true)]
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes)]
|
||||
public float TargetTurbineOutput { get; set; }
|
||||
|
||||
[Serialize(0.0f, true)]
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes)]
|
||||
public float CorrectTurbineOutput { get; set; }
|
||||
|
||||
[Editable, Serialize(true, true)]
|
||||
[Editable, Serialize(true, IsPropertySaveable.Yes)]
|
||||
public bool ExplosionDamagesOtherSubs
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Reactor(Item item, XElement element)
|
||||
public Reactor(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
InitProjSpecific(element);
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(XElement element);
|
||||
partial void InitProjSpecific(ContentXElement element);
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
@@ -248,7 +248,7 @@ namespace Barotrauma.Items.Components
|
||||
//so the player doesn't have to keep adjusting the rate impossibly fast when the load fluctuates heavily
|
||||
if (!MathUtils.NearlyEqual(MaxPowerOutput, 0.0f))
|
||||
{
|
||||
CorrectTurbineOutput += MathHelper.Clamp((Load / MaxPowerOutput * 100.0f) - CorrectTurbineOutput, -10.0f, 10.0f) * deltaTime;
|
||||
CorrectTurbineOutput += MathHelper.Clamp((Load / MaxPowerOutput * 100.0f) - CorrectTurbineOutput, -20.0f, 20.0f) * deltaTime;
|
||||
}
|
||||
|
||||
//calculate tolerances of the meters based on the skills of the user
|
||||
@@ -277,25 +277,6 @@ namespace Barotrauma.Items.Components
|
||||
TurbineOutput = MathHelper.Lerp(turbineOutput, TargetTurbineOutput, deltaTime);
|
||||
|
||||
float temperatureFactor = Math.Min(temperature / 50.0f, 1.0f);
|
||||
currPowerConsumption = -MaxPowerOutput * Math.Min(turbineOutput / 100.0f, temperatureFactor);
|
||||
|
||||
//if the turbine output and coolant flow are the optimal range,
|
||||
//make the generated power slightly adjust according to the load
|
||||
// (-> the reactor can automatically handle small changes in load as long as the values are roughly correct)
|
||||
if (turbineOutput > optimalTurbineOutput.X && turbineOutput < optimalTurbineOutput.Y &&
|
||||
temperature > optimalTemperature.X && temperature < optimalTemperature.Y)
|
||||
{
|
||||
float maxAutoAdjust = maxPowerOutput * 0.1f;
|
||||
autoAdjustAmount = MathHelper.Lerp(
|
||||
autoAdjustAmount,
|
||||
MathHelper.Clamp(-Load - currPowerConsumption, -maxAutoAdjust, maxAutoAdjust),
|
||||
deltaTime * 10.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
autoAdjustAmount = MathHelper.Lerp(autoAdjustAmount, 0.0f, deltaTime * 10.0f);
|
||||
}
|
||||
currPowerConsumption += autoAdjustAmount;
|
||||
|
||||
if (!PowerOn)
|
||||
{
|
||||
@@ -306,35 +287,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
UpdateAutoTemp(2.0f, deltaTime);
|
||||
}
|
||||
float currentLoad = 0.0f;
|
||||
List<Connection> connections = item.Connections;
|
||||
if (connections != null && connections.Count > 0)
|
||||
{
|
||||
foreach (Connection connection in connections)
|
||||
{
|
||||
if (!connection.IsPower) { continue; }
|
||||
foreach (Connection recipient in connection.Recipients)
|
||||
{
|
||||
if (!(recipient.Item is Item it)) { continue; }
|
||||
|
||||
PowerTransfer pt = it.GetComponent<PowerTransfer>();
|
||||
if (pt == null) { continue; }
|
||||
|
||||
//calculate how much external power there is in the grid
|
||||
//(power coming from somewhere else than this reactor, e.g. batteries)
|
||||
float externalPower = Math.Max(CurrPowerConsumption - pt.CurrPowerConsumption, 0) * 0.95f;
|
||||
//reduce the external power from the load to prevent overloading the grid
|
||||
currentLoad = Math.Max(currentLoad, pt.PowerLoad - externalPower);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loadQueue.Enqueue(currentLoad);
|
||||
while (loadQueue.Count() > 60.0f)
|
||||
{
|
||||
Load = loadQueue.Average();
|
||||
loadQueue.Dequeue();
|
||||
}
|
||||
|
||||
float fuelLeft = 0.0f;
|
||||
var containedItems = item.OwnInventory?.AllItems;
|
||||
@@ -345,7 +298,14 @@ namespace Barotrauma.Items.Components
|
||||
if (!item.HasTag("reactorfuel")) { continue; }
|
||||
if (fissionRate > 0.0f)
|
||||
{
|
||||
item.Condition -= fissionRate / 100.0f * fuelConsumptionRate * deltaTime;
|
||||
bool isConnectedToFriendlyOutpost = Level.IsLoadedOutpost &&
|
||||
Item.Submarine?.TeamID == CharacterTeamType.Team1 &&
|
||||
Item.Submarine.GetConnectedSubs().Any(s => s.Info.IsOutpost && s.TeamID == CharacterTeamType.FriendlyNPC);
|
||||
|
||||
if (!isConnectedToFriendlyOutpost)
|
||||
{
|
||||
item.Condition -= fissionRate / 100.0f * fuelConsumptionRate * deltaTime;
|
||||
}
|
||||
}
|
||||
fuelLeft += item.ConditionPercentage;
|
||||
}
|
||||
@@ -405,6 +365,77 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a negative value (indicating the reactor generates power) when querying the power output connection.
|
||||
/// </summary>
|
||||
public override float GetCurrentPowerConsumption(Connection connection = null)
|
||||
{
|
||||
return connection != null && connection.IsPower && connection.IsOutput ? -1 : 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Min and Max power output of the reactor based on tolerance
|
||||
/// </summary>
|
||||
public override PowerRange MinMaxPowerOut(Connection conn, float load)
|
||||
{
|
||||
float tolerance = 1f;
|
||||
|
||||
//If within the optimal output allow for slight output adjustments
|
||||
if (turbineOutput > optimalTurbineOutput.X && turbineOutput < optimalTurbineOutput.Y &&
|
||||
temperature > optimalTemperature.X && temperature < optimalTemperature.Y)
|
||||
{
|
||||
tolerance = 3f;
|
||||
}
|
||||
|
||||
float temperatureFactor = Math.Min(temperature / 50.0f, 1.0f);
|
||||
float minOutput = MaxPowerOutput * Math.Clamp(Math.Min((turbineOutput - tolerance) / 100.0f, temperatureFactor), 0, 1);
|
||||
float maxOutput = MaxPowerOutput * Math.Min((turbineOutput + tolerance) / 100.0f, temperatureFactor);
|
||||
|
||||
minUpdatePowerOut = minOutput;
|
||||
maxUpdatePowerOut = maxOutput;
|
||||
|
||||
float reactorMax = PowerOn ? MaxPowerOutput : maxUpdatePowerOut;
|
||||
|
||||
return new PowerRange(minOutput, maxOutput, reactorMax);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine how much power to output based on the load. The load is divided between reactors according to their maximum output in multi-reactor setups.
|
||||
/// </summary>
|
||||
public override float GetConnectionPowerOut(Connection conn, float power, PowerRange minMaxPower, float load)
|
||||
{
|
||||
//Load must be calculated at this stage instead of at gridResolved to remove influence of lower priority devices
|
||||
float loadLeft = MathHelper.Max(load - power,0);
|
||||
float expectedPower = MathHelper.Clamp(loadLeft, minMaxPower.Min, minMaxPower.Max);
|
||||
|
||||
//Delta ratio of Min and Max power output capability of the grid
|
||||
float ratio = MathHelper.Max((loadLeft - minMaxPower.Min) / (minMaxPower.Max - minMaxPower.Min), 0);
|
||||
if (float.IsInfinity(ratio))
|
||||
{
|
||||
ratio = 0;
|
||||
}
|
||||
|
||||
float output = MathHelper.Clamp(ratio * (maxUpdatePowerOut - minUpdatePowerOut) + minUpdatePowerOut, minUpdatePowerOut, maxUpdatePowerOut);
|
||||
float newLoad = loadLeft;
|
||||
|
||||
//Adjust behaviour for multi reactor setup
|
||||
if (MaxPowerOutput != minMaxPower.ReactorMaxOutput)
|
||||
{
|
||||
float idealLoad = MaxPowerOutput / minMaxPower.ReactorMaxOutput * loadLeft;
|
||||
float loadAdjust = MathHelper.Clamp((ratio - 0.5f) * 25 + idealLoad - (turbineOutput / 100 * MaxPowerOutput), -MaxPowerOutput / 100, MaxPowerOutput / 100);
|
||||
newLoad = MathHelper.Clamp(loadLeft - (expectedPower - output) + loadAdjust, 0, loadLeft);
|
||||
}
|
||||
|
||||
if (float.IsNegative(newLoad))
|
||||
{
|
||||
newLoad = 0.0f;
|
||||
}
|
||||
|
||||
Load = newLoad;
|
||||
currPowerConsumption = -output;
|
||||
return output;
|
||||
}
|
||||
|
||||
private float GetGeneratedHeat(float fissionRate)
|
||||
{
|
||||
return fissionRate * (prevAvailableFuel / 100.0f) * 2.0f;
|
||||
@@ -472,7 +503,6 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (temperature > optimalTemperature.Y)
|
||||
{
|
||||
float prevFireTimer = fireTimer;
|
||||
fireTimer += MathHelper.Lerp(deltaTime * 2.0f, deltaTime, item.Condition / item.MaxCondition);
|
||||
#if SERVER
|
||||
if (fireTimer > Math.Min(5.0f, FireDelay / 2) && blameOnBroken?.Character?.SelectedConstruction == item)
|
||||
@@ -480,9 +510,10 @@ namespace Barotrauma.Items.Components
|
||||
GameMain.Server.KarmaManager.OnReactorOverHeating(item, blameOnBroken.Character, deltaTime);
|
||||
}
|
||||
#endif
|
||||
if (fireTimer >= FireDelay && prevFireTimer < fireDelay)
|
||||
if (fireTimer >= FireDelay)
|
||||
{
|
||||
new FireSource(item.WorldPosition);
|
||||
fireTimer = 0.0f;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -595,7 +626,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return false; }
|
||||
character.AIController.SteeringManager.Reset();
|
||||
bool shutDown = objective.Option.Equals("shutdown", StringComparison.OrdinalIgnoreCase);
|
||||
bool shutDown = objective.Option == "shutdown";
|
||||
|
||||
IsActive = true;
|
||||
|
||||
@@ -624,7 +655,7 @@ namespace Barotrauma.Items.Components
|
||||
var containObjective = AIContainItems<Reactor>(container, character, objective, itemCount: 1, equip: true, removeEmpty: true, spawnItemIfNotFound: !character.IsOnPlayerTeam, dropItemOnDeselected: true);
|
||||
containObjective.Completed += ReportFuelRodCount;
|
||||
containObjective.Abandoned += ReportFuelRodCount;
|
||||
character.Speak(TextManager.Get("DialogReactorFuel"), null, 0.0f, "reactorfuel", 30.0f);
|
||||
character.Speak(TextManager.Get("DialogReactorFuel").Value, null, 0.0f, "reactorfuel".ToIdentifier(), 30.0f);
|
||||
|
||||
void ReportFuelRodCount()
|
||||
{
|
||||
@@ -633,12 +664,12 @@ namespace Barotrauma.Items.Components
|
||||
int remainingFuelRods = Submarine.MainSub.GetItems(false).Count(i => i.HasTag("reactorfuel") && i.Condition > 1);
|
||||
if (remainingFuelRods == 0)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogOutOfFuelRods"), null, 0.0f, "outoffuelrods", 30.0f);
|
||||
character.Speak(TextManager.Get("DialogOutOfFuelRods").Value, null, 0.0f, "outoffuelrods".ToIdentifier(), 30.0f);
|
||||
outOfFuel = true;
|
||||
}
|
||||
else if (remainingFuelRods < 3)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogLowOnFuelRods"), null, 0.0f, "lowonfuelrods", 30.0f);
|
||||
character.Speak(TextManager.Get("DialogLowOnFuelRods").Value, null, 0.0f, "lowonfuelrods".ToIdentifier(), 30.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -655,7 +686,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogReactorIsBroken"), identifier: "reactorisbroken", minDurationBetweenSimilar: 30.0f);
|
||||
character.Speak(TextManager.Get("DialogReactorIsBroken").Value, identifier: "reactorisbroken".ToIdentifier(), minDurationBetweenSimilar: 30.0f);
|
||||
}
|
||||
}
|
||||
if (TooMuchFuel())
|
||||
@@ -676,7 +707,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (lastUser.SelectedConstruction == item && character.IsOnPlayerTeam)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogReactorTaken"), null, 0.0f, "reactortaken", 10.0f);
|
||||
character.Speak(TextManager.Get("DialogReactorTaken").Value, null, 0.0f, "reactortaken".ToIdentifier(), 10.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ namespace Barotrauma.Items.Components
|
||||
get { return connectedTransducers.Select(t => t.Transducer); }
|
||||
}
|
||||
|
||||
[Serialize(DefaultSonarRange, false, description: "The maximum range of the sonar.")]
|
||||
[Serialize(DefaultSonarRange, IsPropertySaveable.No, description: "The maximum range of the sonar.")]
|
||||
public float Range
|
||||
{
|
||||
get { return range; }
|
||||
@@ -92,29 +92,29 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "Should the sonar display the walls of the submarine it is inside.")]
|
||||
[Serialize(false, IsPropertySaveable.No, description: "Should the sonar display the walls of the submarine it is inside.")]
|
||||
public bool DetectSubmarineWalls
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, false, description: "Does the sonar have to be connected to external transducers to work.")]
|
||||
[Editable, Serialize(false, IsPropertySaveable.No, description: "Does the sonar have to be connected to external transducers to work.")]
|
||||
public bool UseTransducers
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, false, description: "Should the sonar view be centered on the transducers or the submarine's center of mass. Only has an effect if UseTransducers is enabled.")]
|
||||
[Editable, Serialize(false, IsPropertySaveable.No, description: "Should the sonar view be centered on the transducers or the submarine's center of mass. Only has an effect if UseTransducers is enabled.")]
|
||||
public bool CenterOnTransducers
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, false, description: "Does the sonar have mineral scanning mode. " +
|
||||
"Only available in-game when the Item has no Steering component.")]
|
||||
[Editable, Serialize(false, IsPropertySaveable.No, description: "Does the sonar have mineral scanning mode. " +
|
||||
"Only available in-game when the Item has no Steering component.")]
|
||||
public bool HasMineralScanner { get; set; }
|
||||
|
||||
public float Zoom
|
||||
@@ -146,7 +146,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override bool RecreateGUIOnResolutionChange => true;
|
||||
|
||||
public Sonar(Item item, XElement element)
|
||||
public Sonar(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
connectedTransducers = new List<ConnectedTransducer>();
|
||||
@@ -155,12 +155,10 @@ namespace Barotrauma.Items.Components
|
||||
CurrentMode = Mode.Passive;
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(XElement element);
|
||||
partial void InitProjSpecific(ContentXElement element);
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
currPowerConsumption = (currentMode == Mode.Active) ? powerConsumption : powerConsumption * PassivePowerConsumption;
|
||||
|
||||
UpdateOnActiveEffects(deltaTime);
|
||||
|
||||
if (UseTransducers)
|
||||
@@ -240,8 +238,19 @@ namespace Barotrauma.Items.Components
|
||||
++pingIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Voltage -= deltaTime;
|
||||
/// <summary>
|
||||
/// Power consumption of the sonar. Only consume power when active and adjust the consumption based on the sonar mode.
|
||||
/// </summary>
|
||||
public override float GetCurrentPowerConsumption(Connection connection = null)
|
||||
{
|
||||
if (connection != powerIn || !IsActive)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (currentMode == Mode.Active) ? powerConsumption : powerConsumption * PassivePowerConsumption;
|
||||
}
|
||||
|
||||
public override bool Use(float deltaTime, Character character = null)
|
||||
@@ -266,7 +275,8 @@ namespace Barotrauma.Items.Components
|
||||
if (DetectSubmarineWalls && c.AnimController.CurrentHull == null && item.CurrentHull != null) { continue; }
|
||||
if (Vector2.DistanceSquared(c.WorldPosition, item.WorldPosition) > range * range) { continue; }
|
||||
|
||||
string directionName = GetDirectionName(c.WorldPosition - item.WorldPosition);
|
||||
#warning This is not the best key for a dictionary.
|
||||
string directionName = GetDirectionName(c.WorldPosition - item.WorldPosition).Value;
|
||||
if (!targetGroups.ContainsKey(directionName))
|
||||
{
|
||||
targetGroups.Add(directionName, new List<Character>());
|
||||
@@ -289,9 +299,10 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (character.IsOnPlayerTeam)
|
||||
{
|
||||
character.Speak(TextManager.GetWithVariables(dialogTag, new string[2] { "[direction]", "[count]" },
|
||||
new string[2] { targetGroup.Key.ToString(), targetGroup.Value.Count.ToString() },
|
||||
new bool[2] { true, false }), null, 0, "sonartarget" + targetGroup.Value[0].ID, 60);
|
||||
character.Speak(TextManager.GetWithVariables(dialogTag,
|
||||
("[direction]", targetGroup.Key.ToString(), FormatCapitals.Yes),
|
||||
("[count]", targetGroup.Value.Count.ToString(), FormatCapitals.No)).Value,
|
||||
null, 0, $"sonartarget{targetGroup.Value[0].ID}".ToIdentifier(), 60);
|
||||
}
|
||||
|
||||
//prevent the character from reporting other targets in the group
|
||||
@@ -304,7 +315,7 @@ namespace Barotrauma.Items.Components
|
||||
return true;
|
||||
}
|
||||
|
||||
private string GetDirectionName(Vector2 dir)
|
||||
private LocalizedString GetDirectionName(Vector2 dir)
|
||||
{
|
||||
float angle = MathUtils.WrapAngleTwoPi((float)-Math.Atan2(dir.Y, dir.X) + MathHelper.PiOver2);
|
||||
|
||||
@@ -338,7 +349,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void ServerRead(ClientNetObject type, IReadMessage msg, Client c)
|
||||
public void ServerEventRead(IReadMessage msg, Client c)
|
||||
{
|
||||
bool isActive = msg.ReadBoolean();
|
||||
bool directionalPing = useDirectionalPing;
|
||||
@@ -383,7 +394,7 @@ namespace Barotrauma.Items.Components
|
||||
#endif
|
||||
}
|
||||
|
||||
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
|
||||
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
msg.Write(currentMode == Mode.Active);
|
||||
if (currentMode == Mode.Active)
|
||||
|
||||
+11
-3
@@ -10,7 +10,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public Sonar ConnectedSonar;
|
||||
|
||||
public SonarTransducer(Item item, XElement element) : base(item, element)
|
||||
public SonarTransducer(Item item, ContentXElement element) : base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
}
|
||||
@@ -19,8 +19,6 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
UpdateOnActiveEffects(deltaTime);
|
||||
|
||||
CurrPowerConsumption = powerConsumption * (ConnectedSonar?.CurrentMode == Sonar.Mode.Active ? 1.0f : Sonar.PassivePowerConsumption);
|
||||
|
||||
if (Voltage >= MinVoltage)
|
||||
{
|
||||
sendSignalTimer += deltaTime;
|
||||
@@ -31,5 +29,15 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override float GetCurrentPowerConsumption(Connection connection = null)
|
||||
{
|
||||
if (connection != powerIn || !IsActive)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return PowerConsumption * (ConnectedSonar?.CurrentMode == Sonar.Mode.Active ? 1.0f : Sonar.PassivePowerConsumption);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Networking;
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
@@ -106,7 +106,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
[Editable(0.0f, 1.0f, decimals: 4),
|
||||
Serialize(0.5f, true, description: "How full the ballast tanks should be when the submarine is not being steered upwards/downwards."
|
||||
Serialize(0.5f, IsPropertySaveable.Yes, description: "How full the ballast tanks should be when the submarine is not being steered upwards/downwards."
|
||||
+ " Can be used to compensate if the ballast tanks are too large/small relative to the size of the submarine.")]
|
||||
public float NeutralBallastLevel
|
||||
{
|
||||
@@ -117,7 +117,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(1000.0f, true, description: "How close the docking port has to be to another docking port for the docking mode to become active.")]
|
||||
[Serialize(1000.0f, IsPropertySaveable.Yes, description: "How close the docking port has to be to another docking port for the docking mode to become active.")]
|
||||
public float DockingAssistThreshold
|
||||
{
|
||||
get;
|
||||
@@ -225,20 +225,20 @@ namespace Barotrauma.Items.Components
|
||||
var dockingConnection = item.Connections.FirstOrDefault(c => c.Name == "toggle_docking");
|
||||
if (dockingConnection != null)
|
||||
{
|
||||
var connectedPorts = item.GetConnectedComponentsRecursive<DockingPort>(dockingConnection);
|
||||
var connectedPorts = item.GetConnectedComponentsRecursive<DockingPort>(dockingConnection, allowTraversingBackwards: false);
|
||||
DockingSources.AddRange(connectedPorts.Where(p => p.Item.Submarine != null && !p.Item.Submarine.Info.IsOutpost));
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
public Steering(Item item, XElement element)
|
||||
public Steering(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
InitProjSpecific(element);
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(XElement element);
|
||||
partial void InitProjSpecific(ContentXElement element);
|
||||
|
||||
public override void OnItemLoaded()
|
||||
{
|
||||
@@ -271,13 +271,9 @@ namespace Barotrauma.Items.Components
|
||||
item.CreateClientEvent(this);
|
||||
correctionTimer = CorrectionDelay;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
#if SERVER
|
||||
if (GameMain.Server != null)
|
||||
{
|
||||
item.CreateServerEvent(this);
|
||||
}
|
||||
item.CreateServerEvent(this);
|
||||
#endif
|
||||
|
||||
networkUpdateTimer = 0.1f;
|
||||
@@ -292,8 +288,6 @@ namespace Barotrauma.Items.Components
|
||||
controlledSub = sonar.ConnectedTransducers.Any() ? sonar.ConnectedTransducers.First().Item.Submarine : null;
|
||||
}
|
||||
|
||||
currPowerConsumption = powerConsumption;
|
||||
|
||||
if (Voltage < MinVoltage) { return; }
|
||||
|
||||
if (user != null && user.Removed)
|
||||
@@ -405,7 +399,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
float userSkill = Math.Max(user.GetSkillLevel("helm"), 1.0f) / 100.0f;
|
||||
user.Info.IncreaseSkillLevel(
|
||||
"helm",
|
||||
"helm".ToIdentifier(),
|
||||
SkillSettings.Current.SkillIncreasePerSecondWhenSteering / userSkill * deltaTime);
|
||||
}
|
||||
|
||||
@@ -724,7 +718,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (user != character && user != null && user.SelectedConstruction == item && character.IsOnPlayerTeam)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogSteeringTaken"), null, 0.0f, "steeringtaken", 10.0f);
|
||||
character.Speak(TextManager.Get("DialogSteeringTaken").Value, null, 0.0f, "steeringtaken".ToIdentifier(), 10.0f);
|
||||
}
|
||||
}
|
||||
user = character;
|
||||
@@ -738,7 +732,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogNavTerminalIsBroken"), identifier: "navterminalisbroken", minDurationBetweenSimilar: 30.0f);
|
||||
character.Speak(TextManager.Get("DialogNavTerminalIsBroken").Value, identifier: "navterminalisbroken".ToIdentifier(), minDurationBetweenSimilar: 30.0f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -748,64 +742,72 @@ namespace Barotrauma.Items.Components
|
||||
AutoPilot = true;
|
||||
}
|
||||
IncreaseSkillLevel(user, deltaTime);
|
||||
switch (objective.Option.ToLowerInvariant())
|
||||
if (objective.Option == "maintainposition")
|
||||
{
|
||||
case "maintainposition":
|
||||
if (objective.Override)
|
||||
{
|
||||
SetMaintainPosition();
|
||||
}
|
||||
break;
|
||||
case "navigateback":
|
||||
if (Level.IsLoadedOutpost) { break; }
|
||||
if (objective.Override)
|
||||
{
|
||||
SetMaintainPosition();
|
||||
}
|
||||
}
|
||||
else if (!Level.IsLoadedOutpost)
|
||||
{
|
||||
if (objective.Option == "navigateback")
|
||||
{
|
||||
if (DockingSources.Any(d => d.Docked))
|
||||
{
|
||||
item.SendSignal("1", "toggle_docking");
|
||||
}
|
||||
|
||||
if (objective.Override)
|
||||
{
|
||||
if (MaintainPos || LevelEndSelected || !LevelStartSelected || navigateTactically)
|
||||
{
|
||||
unsentChanges = true;
|
||||
}
|
||||
|
||||
SetDestinationLevelStart();
|
||||
}
|
||||
break;
|
||||
case "navigatetodestination":
|
||||
if (Level.IsLoadedOutpost) { break; }
|
||||
}
|
||||
else if (objective.Option == "navigatetodestination")
|
||||
{
|
||||
if (DockingSources.Any(d => d.Docked))
|
||||
{
|
||||
item.SendSignal("1", "toggle_docking");
|
||||
}
|
||||
|
||||
if (objective.Override)
|
||||
{
|
||||
if (MaintainPos || !LevelEndSelected || LevelStartSelected || navigateTactically)
|
||||
{
|
||||
unsentChanges = true;
|
||||
}
|
||||
|
||||
SetDestinationLevelEnd();
|
||||
}
|
||||
break;
|
||||
case "navigatetactical":
|
||||
if (Level.IsLoadedOutpost) { break; }
|
||||
}
|
||||
else if (objective.Option == "navigatetactical")
|
||||
{
|
||||
if (DockingSources.Any(d => d.Docked))
|
||||
{
|
||||
item.SendSignal("1", "toggle_docking");
|
||||
}
|
||||
|
||||
if (objective.Override)
|
||||
{
|
||||
if (MaintainPos || LevelEndSelected || LevelStartSelected || !navigateTactically)
|
||||
{
|
||||
unsentChanges = true;
|
||||
}
|
||||
|
||||
SetDestinationTactical();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sonar?.AIOperate(deltaTime, character, objective);
|
||||
if (!MaintainPos && showIceSpireWarning && character.IsOnPlayerTeam)
|
||||
{
|
||||
character.Speak(TextManager.Get("dialogicespirespottedsonar"), null, 0.0f, "icespirespottedsonar", 60.0f);
|
||||
character.Speak(TextManager.Get("dialogicespirespottedsonar").Value, null, 0.0f, "icespirespottedsonar".ToIdentifier(), 60.0f);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Barotrauma.Items.Components
|
||||
set { oxygenFlow = Math.Max(value, 0.0f); }
|
||||
}
|
||||
|
||||
public Vent (Item item, XElement element) : base(item, element) { }
|
||||
public Vent (Item item, ContentXElement element) : base(item, element) { }
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
|
||||
@@ -4,10 +4,10 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
class NameTag : ItemComponent
|
||||
{
|
||||
[InGameEditable(MaxLength = 32), Serialize("", false, description: "Name written on the tag.", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable(MaxLength = 32), Serialize("", IsPropertySaveable.No, description: "Name written on the tag.", alwaysUseInstanceValues: true)]
|
||||
public string WrittenName { get; set; }
|
||||
|
||||
public NameTag(Item item, XElement element) : base(item, element)
|
||||
public NameTag(Item item, ContentXElement element) : base(item, element)
|
||||
{
|
||||
AllowInGameEditing = true;
|
||||
DrawHudWhenEquipped = true;
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace Barotrauma.Items.Components
|
||||
public Vector2 Offset;
|
||||
public float Size;
|
||||
|
||||
public PlantSlot(XElement element)
|
||||
public PlantSlot(ContentXElement element)
|
||||
{
|
||||
Offset = element.GetAttributeVector2("offset", Vector2.Zero);
|
||||
Size = element.GetAttributeFloat("size", 0.5f);
|
||||
@@ -64,14 +64,14 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private float fertilizer;
|
||||
|
||||
[Serialize(0f, true, "How much fertilizer the planter has.")]
|
||||
[Serialize(0f, IsPropertySaveable.Yes, "How much fertilizer the planter has.")]
|
||||
public float Fertilizer
|
||||
{
|
||||
get => fertilizer;
|
||||
set => fertilizer = Math.Clamp(value, 0, FertilizerCapacity);
|
||||
}
|
||||
|
||||
[Serialize(100f, true, "How much fertilizer can the planter hold.")]
|
||||
[Serialize(100f, IsPropertySaveable.Yes, "How much fertilizer can the planter hold.")]
|
||||
public float FertilizerCapacity { get; set; }
|
||||
|
||||
public Growable?[] GrowableSeeds = new Growable?[0];
|
||||
@@ -81,11 +81,11 @@ namespace Barotrauma.Items.Components
|
||||
private ItemContainer? container;
|
||||
private float growthTickTimer;
|
||||
|
||||
public Planter(Item item, XElement element) : base(item, element)
|
||||
public Planter(Item item, ContentXElement element) : base(item, element)
|
||||
{
|
||||
canBePicked = true;
|
||||
SerializableProperty.DeserializeProperties(this, element);
|
||||
foreach (XElement subElement in element.Elements())
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
@@ -117,7 +117,7 @@ namespace Barotrauma.Items.Components
|
||||
GrowableSeeds = new Growable[container.Capacity];
|
||||
}
|
||||
|
||||
public override bool HasRequiredItems(Character character, bool addMessage, string? msg = null)
|
||||
public override bool HasRequiredItems(Character character, bool addMessage, LocalizedString? msg = null)
|
||||
{
|
||||
if (container?.Inventory == null) { return false; }
|
||||
|
||||
@@ -212,7 +212,7 @@ namespace Barotrauma.Items.Components
|
||||
if (!anyDecayed || seed.Decayed || seed.FullyGrown)
|
||||
{
|
||||
container?.Inventory.RemoveItem(seed.Item);
|
||||
Entity.Spawner?.AddToRemoveQueue(seed.Item);
|
||||
Entity.Spawner?.AddItemToRemoveQueue(seed.Item);
|
||||
GrowableSeeds[i] = null;
|
||||
ApplyStatusEffects(ActionType.OnPicked, 1.0f, character);
|
||||
return true;
|
||||
|
||||
+152
-99
@@ -2,7 +2,6 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -11,9 +10,7 @@ namespace Barotrauma.Items.Components
|
||||
//[power/min]
|
||||
private float capacity;
|
||||
|
||||
private float charge;
|
||||
|
||||
//private float rechargeVoltage;
|
||||
private float charge, prevCharge;
|
||||
|
||||
//how fast the battery can be recharged
|
||||
private float maxRechargeSpeed;
|
||||
@@ -27,45 +24,52 @@ namespace Barotrauma.Items.Components
|
||||
protected Vector2 indicatorPosition, indicatorSize;
|
||||
|
||||
protected bool isHorizontal;
|
||||
|
||||
|
||||
protected override PowerPriority Priority { get { return PowerPriority.Battery; } }
|
||||
|
||||
private float currPowerOutput;
|
||||
public float CurrPowerOutput
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
get { return currPowerOutput; }
|
||||
private set
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(value >= 0.0f, $"Tried to set PowerContainer's output to a negative value ({value})");
|
||||
currPowerOutput = Math.Max(0, value);
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize("0,0", true, description: "The position of the progress bar indicating the charge of the item. In pixels as an offset from the upper left corner of the sprite.")]
|
||||
[Serialize("0,0", IsPropertySaveable.Yes, description: "The position of the progress bar indicating the charge of the item. In pixels as an offset from the upper left corner of the sprite.")]
|
||||
public Vector2 IndicatorPosition
|
||||
{
|
||||
get { return indicatorPosition; }
|
||||
set { indicatorPosition = value; }
|
||||
}
|
||||
|
||||
[Serialize("0,0", true, description: "The size of the progress bar indicating the charge of the item (in pixels).")]
|
||||
[Serialize("0,0", IsPropertySaveable.Yes, description: "The size of the progress bar indicating the charge of the item (in pixels).")]
|
||||
public Vector2 IndicatorSize
|
||||
{
|
||||
get { return indicatorSize; }
|
||||
set { indicatorSize = value; }
|
||||
}
|
||||
|
||||
[Serialize(false, true, description: "Should the progress bar indicating the charge of the item fill up horizontally or vertically.")]
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Should the progress bar indicating the charge of the item fill up horizontally or vertically.")]
|
||||
public bool IsHorizontal
|
||||
{
|
||||
get { return isHorizontal; }
|
||||
set { isHorizontal = value; }
|
||||
}
|
||||
|
||||
[Editable, Serialize(10.0f, true, description: "Maximum output of the device when fully charged (kW).")]
|
||||
[Editable, Serialize(10.0f, IsPropertySaveable.Yes, description: "Maximum output of the device when fully charged (kW).")]
|
||||
public float MaxOutPut { set; get; }
|
||||
|
||||
[Editable, Serialize(10.0f, true, description: "The maximum capacity of the device (kW * min). For example, a value of 1000 means the device can output 100 kilowatts of power for 10 minutes, or 1000 kilowatts for 1 minute.")]
|
||||
[Editable, Serialize(10.0f, IsPropertySaveable.Yes, description: "The maximum capacity of the device (kW * min). For example, a value of 1000 means the device can output 100 kilowatts of power for 10 minutes, or 1000 kilowatts for 1 minute.")]
|
||||
public float Capacity
|
||||
{
|
||||
get { return capacity; }
|
||||
set { capacity = Math.Max(value, 1.0f); }
|
||||
}
|
||||
|
||||
[Editable, Serialize(0.0f, true, description: "The current charge of the device.")]
|
||||
[Editable, Serialize(0.0f, IsPropertySaveable.Yes, description: "The current charge of the device.")]
|
||||
public float Charge
|
||||
{
|
||||
get { return charge; }
|
||||
@@ -87,14 +91,14 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public float ChargePercentage => MathUtils.Percentage(Charge, Capacity);
|
||||
|
||||
[Editable, Serialize(10.0f, true, description: "How fast the device can be recharged. For example, a recharge speed of 100 kW and a capacity of 1000 kW*min would mean it takes 10 minutes to fully charge the device.")]
|
||||
[Editable, Serialize(10.0f, IsPropertySaveable.Yes, description: "How fast the device can be recharged. For example, a recharge speed of 100 kW and a capacity of 1000 kW*min would mean it takes 10 minutes to fully charge the device.")]
|
||||
public float MaxRechargeSpeed
|
||||
{
|
||||
get { return maxRechargeSpeed; }
|
||||
set { maxRechargeSpeed = Math.Max(value, 1.0f); }
|
||||
}
|
||||
|
||||
[Editable, Serialize(10.0f, true, description: "The current recharge speed of the device.")]
|
||||
[Editable, Serialize(10.0f, IsPropertySaveable.Yes, description: "The current recharge speed of the device.")]
|
||||
public float RechargeSpeed
|
||||
{
|
||||
get { return rechargeSpeed; }
|
||||
@@ -110,14 +114,14 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(false, true, description: "If true, the recharge speed (and power consumption) of the device goes up exponentially as the recharge rate is increased.")]
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "If true, the recharge speed (and power consumption) of the device goes up exponentially as the recharge rate is increased.")]
|
||||
public bool ExponentialRechargeSpeed { get; set; }
|
||||
|
||||
[Editable(minValue: 0.0f, maxValue: 10.0f, decimals: 2), Serialize(0.5f, true)]
|
||||
[Editable(minValue: 0.0f, maxValue: 10.0f, decimals: 2), Serialize(0.5f, IsPropertySaveable.Yes)]
|
||||
public float RechargeAdjustSpeed { get; set; }
|
||||
|
||||
private float efficiency;
|
||||
[Editable(minValue: 0.0f, maxValue: 1.0f, decimals: 2), Serialize(0.95f, true, description: "The amount of power you can get out of a item relative to the amount of power that's put into it.")]
|
||||
[Editable(minValue: 0.0f, maxValue: 1.0f, decimals: 2), Serialize(0.95f, IsPropertySaveable.Yes, description: "The amount of power you can get out of a item relative to the amount of power that's put into it.")]
|
||||
public float Efficiency
|
||||
{
|
||||
get { return efficiency; }
|
||||
@@ -130,11 +134,12 @@ namespace Barotrauma.Items.Components
|
||||
private bool isRunning;
|
||||
public bool HasBeenTuned { get; private set; }
|
||||
|
||||
public PowerContainer(Item item, XElement element)
|
||||
public PowerContainer(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
InitProjSpecific();
|
||||
prevCharge = Charge;
|
||||
}
|
||||
|
||||
partial void InitProjSpecific();
|
||||
@@ -154,92 +159,138 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
isRunning = true;
|
||||
float chargeRatio = charge / capacity;
|
||||
float gridPower = 0.0f;
|
||||
float gridLoad = 0.0f;
|
||||
foreach (Connection c in item.Connections)
|
||||
{
|
||||
if (!c.IsPower || !c.IsOutput) { continue; }
|
||||
foreach (Connection c2 in c.Recipients)
|
||||
{
|
||||
if (c2.Item.Condition <= 0.0f) { continue; }
|
||||
|
||||
PowerTransfer pt = c2.Item.GetComponent<PowerTransfer>();
|
||||
if (pt == null)
|
||||
{
|
||||
foreach (Powered powered in c2.Item.GetComponents<Powered>())
|
||||
{
|
||||
if (!powered.IsActive) continue;
|
||||
gridLoad += powered.CurrPowerConsumption;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!pt.IsActive || !pt.CanTransfer) { continue; }
|
||||
gridPower -= pt.CurrPowerConsumption;
|
||||
gridLoad += pt.PowerLoad;
|
||||
}
|
||||
}
|
||||
|
||||
if (chargeRatio > 0.0f)
|
||||
{
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
}
|
||||
|
||||
if (charge >= capacity)
|
||||
float loadReading = 0;
|
||||
if (powerOut != null && powerOut.Grid != null)
|
||||
{
|
||||
//rechargeVoltage = 0.0f;
|
||||
charge = capacity;
|
||||
CurrPowerConsumption = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
float missingCharge = capacity - charge;
|
||||
float targetRechargeSpeed = rechargeSpeed;
|
||||
if (ExponentialRechargeSpeed)
|
||||
{
|
||||
targetRechargeSpeed = MathF.Pow(rechargeSpeed / maxRechargeSpeed, 2) * maxRechargeSpeed;
|
||||
}
|
||||
if (missingCharge < 1.0f)
|
||||
{
|
||||
targetRechargeSpeed *= missingCharge;
|
||||
}
|
||||
if (currPowerConsumption < targetRechargeSpeed)
|
||||
{
|
||||
currPowerConsumption = Math.Min(currPowerConsumption + deltaTime * maxRechargeSpeed * RechargeAdjustSpeed, targetRechargeSpeed);
|
||||
}
|
||||
else
|
||||
{
|
||||
currPowerConsumption = Math.Max(currPowerConsumption - deltaTime * maxRechargeSpeed * RechargeAdjustSpeed, targetRechargeSpeed);
|
||||
}
|
||||
Charge += currPowerConsumption * Math.Min(Voltage, 1.0f) / 3600.0f * efficiency;
|
||||
}
|
||||
|
||||
if (charge <= 0.0f)
|
||||
{
|
||||
CurrPowerOutput = 0.0f;
|
||||
charge = 0.0f;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
//output starts dropping when the charge is less than 10%
|
||||
float maxOutputRatio = 1.0f;
|
||||
if (chargeRatio < 0.1f)
|
||||
{
|
||||
maxOutputRatio = Math.Max(chargeRatio * 10.0f, 0.0f);
|
||||
}
|
||||
|
||||
CurrPowerOutput += (gridLoad - gridPower) * deltaTime;
|
||||
|
||||
float maxOutput = Math.Min(MaxOutPut * maxOutputRatio, gridLoad);
|
||||
CurrPowerOutput = MathHelper.Clamp(CurrPowerOutput, 0.0f, maxOutput);
|
||||
Charge -= CurrPowerOutput / 3600.0f;
|
||||
loadReading = powerOut.Grid.Load;
|
||||
}
|
||||
|
||||
item.SendSignal(((int)Math.Round(CurrPowerOutput)).ToString(), "power_value_out");
|
||||
item.SendSignal(((int)Math.Round(loadReading)).ToString(), "load_value_out");
|
||||
item.SendSignal(((int)Math.Round(Charge)).ToString(), "charge");
|
||||
item.SendSignal(((int)Math.Round(Charge / capacity * 100)).ToString(), "charge_%");
|
||||
item.SendSignal(((int)Math.Round(RechargeSpeed / maxRechargeSpeed * 100)).ToString(), "charge_rate");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the power consumption if checking the powerIn connection, or a negative value if the output can provide power when checking powerOut.
|
||||
/// Power consumption is proportional to set recharge speed and if there is less than max charge.
|
||||
/// </summary>
|
||||
public override float GetCurrentPowerConsumption(Connection connection = null)
|
||||
{
|
||||
if (connection == powerIn)
|
||||
{
|
||||
//Don't draw power if fully charged
|
||||
if (charge >= capacity)
|
||||
{
|
||||
charge = capacity;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (item.Condition <= 0.0f) { return 0.0f; }
|
||||
|
||||
float missingCharge = capacity - charge;
|
||||
float targetRechargeSpeed = rechargeSpeed;
|
||||
|
||||
if (ExponentialRechargeSpeed)
|
||||
{
|
||||
targetRechargeSpeed = MathF.Pow(rechargeSpeed / maxRechargeSpeed, 2) * maxRechargeSpeed;
|
||||
}
|
||||
//For the last kwMin scale the recharge rate linearly to prevent overcharging and to have a smooth cutoff
|
||||
if (missingCharge < 1.0f)
|
||||
{
|
||||
targetRechargeSpeed *= missingCharge;
|
||||
}
|
||||
|
||||
return MathHelper.Clamp(targetRechargeSpeed, 0, MaxRechargeSpeed);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrPowerOutput = 0;
|
||||
return charge > 0 ? -1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Minimum and maximum output for the queried connection.
|
||||
/// Powerin min max equals CurrPowerConsumption as its abnormal for there to be power out.
|
||||
/// PowerOut min power out is zero and max is the maxout unless below 10% charge where
|
||||
/// the output is scaled relative to the 10% charge.
|
||||
/// </summary>
|
||||
/// <param name="connection">Connection being queried</param>
|
||||
/// <param name="load">Current grid load</param>
|
||||
/// <returns>Minimum and maximum power output for the connection</returns>
|
||||
public override PowerRange MinMaxPowerOut(Connection connection, float load = 0)
|
||||
{
|
||||
if (connection == powerOut)
|
||||
{
|
||||
float maxOutput;
|
||||
float chargeRatio = prevCharge / capacity;
|
||||
if (chargeRatio < 0.1f)
|
||||
{
|
||||
maxOutput = Math.Max(chargeRatio * 10.0f, 0.0f) * MaxOutPut;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxOutput = MaxOutPut;
|
||||
}
|
||||
|
||||
//Limit max power out to not exceed the charge of the container
|
||||
maxOutput = Math.Min(maxOutput, prevCharge * 60 / UpdateInterval);
|
||||
return new PowerRange(0.0f, maxOutput);
|
||||
}
|
||||
|
||||
return PowerRange.Zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalized power out from the container for the connection, provided the given grid information
|
||||
/// Output power based on the maxpower all batteries can output. So all batteries can
|
||||
/// equally share powerout based on their output capabilities.
|
||||
/// </summary>
|
||||
/// <param name="connection"></param>
|
||||
/// <param name="power"></param>
|
||||
/// <param name="minMaxPower"></param>
|
||||
/// <param name="load"></param>
|
||||
/// <returns></returns>
|
||||
public override float GetConnectionPowerOut(Connection connection, float power, PowerRange minMaxPower, float load)
|
||||
{
|
||||
//Only power out connection can provide power and Max poweroutput can't be negative
|
||||
if (connection == powerOut && minMaxPower.Max > 0)
|
||||
{
|
||||
//Set power output based on the relative max power output capabilities and load demand
|
||||
CurrPowerOutput = MathHelper.Clamp((load - power) / minMaxPower.Max, 0, 1) * MinMaxPowerOut(connection, load).Max;
|
||||
return CurrPowerOutput;
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When the corresponding grid connection is resolved, adjust the container's charge.
|
||||
/// </summary>
|
||||
public override void GridResolved(Connection conn)
|
||||
{
|
||||
if (conn == powerIn)
|
||||
{
|
||||
//Increase charge based on how much power came in from the grid
|
||||
Charge += (CurrPowerConsumption * Voltage) / 60 * UpdateInterval * efficiency;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Decrease charge based on how much power is leaving the device
|
||||
Charge = Math.Clamp(Charge - CurrPowerOutput / 60 * UpdateInterval, 0, Capacity);
|
||||
prevCharge = Charge;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return false; }
|
||||
@@ -250,8 +301,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
if (HasBeenTuned) { return true; }
|
||||
|
||||
float targetRatio = string.IsNullOrEmpty(objective.Option) || objective.Option.Equals("charge", StringComparison.OrdinalIgnoreCase) ? aiRechargeTargetRatio : -1;
|
||||
if (targetRatio > 0 || float.TryParse(objective.Option, out targetRatio))
|
||||
float targetRatio = objective.Option.IsEmpty || objective.Option == "charge" ? aiRechargeTargetRatio : -1;
|
||||
if (targetRatio > 0 || float.TryParse(objective.Option.Value, out targetRatio))
|
||||
{
|
||||
if (Math.Abs(rechargeSpeed - maxRechargeSpeed * targetRatio) > 0.05f)
|
||||
{
|
||||
@@ -267,9 +318,10 @@ namespace Barotrauma.Items.Components
|
||||
#endif
|
||||
if (character.IsOnPlayerTeam)
|
||||
{
|
||||
character.Speak(TextManager.GetWithVariables("DialogChargeBatteries", new string[2] { "[itemname]", "[rate]" },
|
||||
new string[2] { item.Name, ((int)(rechargeSpeed / maxRechargeSpeed * 100.0f)).ToString() },
|
||||
new bool[2] { true, false }), null, 1.0f, "chargebattery", 10.0f);
|
||||
character.Speak(TextManager.GetWithVariables("DialogChargeBatteries",
|
||||
("[itemname]", item.Name, FormatCapitals.Yes),
|
||||
("[rate]", ((int)(rechargeSpeed / maxRechargeSpeed * 100.0f)).ToString(), FormatCapitals.No)).Value,
|
||||
null, 1.0f, "chargebattery".ToIdentifier(), 10.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -289,9 +341,10 @@ namespace Barotrauma.Items.Components
|
||||
#endif
|
||||
if (character.IsOnPlayerTeam)
|
||||
{
|
||||
character.Speak(TextManager.GetWithVariables("DialogStopChargingBatteries", new string[2] { "[itemname]", "[rate]" },
|
||||
new string[2] { item.Name, ((int)(rechargeSpeed / maxRechargeSpeed * 100.0f)).ToString() },
|
||||
new bool[2] { true, false }), null, 1.0f, "chargebattery", 10.0f);
|
||||
character.Speak(TextManager.GetWithVariables("DialogStopChargingBatteries",
|
||||
("[itemname]", item.Name, FormatCapitals.Yes),
|
||||
("[rate]", ((int)(rechargeSpeed / maxRechargeSpeed * 100.0f)).ToString(), FormatCapitals.No)).Value,
|
||||
null, 1.0f, "chargebattery".ToIdentifier(), 10.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,18 +26,25 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public float PowerLoad
|
||||
{
|
||||
get { return powerLoad; }
|
||||
get
|
||||
{
|
||||
if (this is RelayComponent || PowerConnections.Count == 0 || PowerConnections[0].Grid == null)
|
||||
{
|
||||
return powerLoad;
|
||||
}
|
||||
return PowerConnections[0].Grid.Load;
|
||||
}
|
||||
set { powerLoad = value; }
|
||||
}
|
||||
|
||||
[Editable, Serialize(true, true, description: "Can the item be damaged if too much power is supplied to the power grid.")]
|
||||
[Editable, Serialize(true, IsPropertySaveable.Yes, description: "Can the item be damaged if too much power is supplied to the power grid.")]
|
||||
public bool CanBeOverloaded
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable(MinValueFloat = 1.0f), Serialize(2.0f, true, description:
|
||||
[Editable(MinValueFloat = 1.0f), Serialize(2.0f, IsPropertySaveable.Yes, description:
|
||||
"How much power has to be supplied to the grid relative to the load before item starts taking damage. "
|
||||
+ "E.g. a value of 2 means that the grid has to be receiving twice as much power as the devices in the grid are consuming.")]
|
||||
public float OverloadVoltage
|
||||
@@ -46,14 +53,14 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(0.15f, true, description: "The probability for a fire to start when the item breaks."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f)]
|
||||
[Serialize(0.15f, IsPropertySaveable.Yes, description: "The probability for a fire to start when the item breaks."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f)]
|
||||
public float FireProbability
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "Is the item currently overloaded. Intended to be used by StatusEffect conditionals (setting the value from XML is not recommended).")]
|
||||
[Serialize(false, IsPropertySaveable.No, description: "Is the item currently overloaded. Intended to be used by StatusEffect conditionals (setting the value from XML is not recommended).")]
|
||||
public bool Overload
|
||||
{
|
||||
get;
|
||||
@@ -62,6 +69,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private float extraLoad;
|
||||
private float extraLoadSetTime;
|
||||
|
||||
/// <summary>
|
||||
/// Additional load coming from somewhere else than the devices connected to the junction box (e.g. ballast flora or piezo crystals).
|
||||
/// Goes back to zero automatically if you stop setting the value.
|
||||
@@ -71,7 +79,7 @@ namespace Barotrauma.Items.Components
|
||||
get { return extraLoad; }
|
||||
set
|
||||
{
|
||||
extraLoad = Math.Max(value, 0.0f);
|
||||
extraLoad = value;
|
||||
extraLoadSetTime = (float)Timing.TotalTime;
|
||||
}
|
||||
}
|
||||
@@ -112,7 +120,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public PowerTransfer(Item item, XElement element)
|
||||
public PowerTransfer(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
@@ -170,7 +178,15 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (Timing.TotalTime > extraLoadSetTime + 1.0)
|
||||
{
|
||||
extraLoad = Math.Max(extraLoad - 1000.0f * deltaTime, 0);
|
||||
//Decay the extra load to 0 from either positive or negative
|
||||
if (extraLoad > 0)
|
||||
{
|
||||
extraLoad = Math.Max(extraLoad - 1000.0f * deltaTime, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
extraLoad = Math.Min(extraLoad + 1000.0f * deltaTime, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!CanTransfer) { return; }
|
||||
@@ -183,24 +199,40 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
|
||||
//if the item can't be fixed, don't allow it to break
|
||||
if (!item.Repairables.Any() || !CanBeOverloaded) { return; }
|
||||
|
||||
if (prevSentPowerValue != (int)-CurrPowerConsumption || powerSignal == null)
|
||||
float powerReadingOut = 0;
|
||||
float loadReadingOut = ExtraLoad;
|
||||
if (powerLoad < 0)
|
||||
{
|
||||
prevSentPowerValue = (int)Math.Round(-CurrPowerConsumption);
|
||||
powerReadingOut = -powerLoad;
|
||||
loadReadingOut = 0;
|
||||
}
|
||||
|
||||
if (powerOut != null && powerOut.Grid != null)
|
||||
{
|
||||
powerReadingOut = powerOut.Grid.Power;
|
||||
loadReadingOut = powerOut.Grid.Load;
|
||||
}
|
||||
|
||||
if (prevSentPowerValue != (int)powerReadingOut || powerSignal == null)
|
||||
{
|
||||
prevSentPowerValue = (int)Math.Round(powerReadingOut);
|
||||
powerSignal = prevSentPowerValue.ToString();
|
||||
}
|
||||
if (prevSentLoadValue != (int)powerLoad || loadSignal == null)
|
||||
if (prevSentLoadValue != (int)loadReadingOut || loadSignal == null)
|
||||
{
|
||||
prevSentLoadValue = (int)Math.Round(powerLoad);
|
||||
prevSentLoadValue = (int)Math.Round(loadReadingOut);
|
||||
loadSignal = prevSentLoadValue.ToString();
|
||||
}
|
||||
item.SendSignal(powerSignal, "power_value_out");
|
||||
item.SendSignal(loadSignal, "load_value_out");
|
||||
|
||||
//if the item can't be fixed, don't allow it to break
|
||||
if (!item.Repairables.Any() || !CanBeOverloaded) { return; }
|
||||
|
||||
float maxOverVoltage = Math.Max(OverloadVoltage, 1.0f);
|
||||
Overload = -currPowerConsumption > Math.Max(powerLoad, 200.0f) * maxOverVoltage;
|
||||
|
||||
Overload = Voltage > maxOverVoltage;
|
||||
|
||||
if (Overload && (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer))
|
||||
{
|
||||
if (overloadCooldownTimer > 0.0f)
|
||||
@@ -239,6 +271,11 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override float GetConnectionPowerOut(Connection conn, float power, PowerRange minMaxPower, float load)
|
||||
{
|
||||
return conn == powerOut ? PowerConsumption + ExtraLoad : 0;
|
||||
}
|
||||
|
||||
public override bool Pick(Character picker)
|
||||
{
|
||||
return picker != null;
|
||||
@@ -376,25 +413,6 @@ namespace Barotrauma.Items.Components
|
||||
SetAllConnectionsDirty();
|
||||
}
|
||||
|
||||
public override void ReceivePowerProbeSignal(Connection connection, Item source, float power)
|
||||
{
|
||||
//we've already received this signal
|
||||
if (lastPowerProbeRecipients.Contains(this)) { return; }
|
||||
if (item.Condition <= 0.0f) { return; }
|
||||
|
||||
lastPowerProbeRecipients.Add(this);
|
||||
|
||||
if (power < 0.0f)
|
||||
{
|
||||
powerLoad -= power;
|
||||
}
|
||||
else
|
||||
{
|
||||
currPowerConsumption -= power;
|
||||
}
|
||||
powerOut?.SendPowerProbeSignal(source, power);
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(Signal signal, Connection connection)
|
||||
{
|
||||
if (item.Condition <= 0.0f || connection.IsPower) { return; }
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
#if CLIENT
|
||||
@@ -8,10 +7,57 @@ using Barotrauma.Sounds;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Order in which power sources will provide to a grid, lower number is higher priority
|
||||
/// </summary>
|
||||
public enum PowerPriority
|
||||
{
|
||||
Default = 0, // Use for status effects and/or extraload
|
||||
Reactor = 1,
|
||||
Relay = 3,
|
||||
Battery = 5
|
||||
}
|
||||
|
||||
readonly struct PowerRange
|
||||
{
|
||||
public readonly static PowerRange Zero = default;
|
||||
|
||||
public readonly float Min;
|
||||
public readonly float Max;
|
||||
|
||||
/// <summary>
|
||||
/// Used by reactors to communicate their maximum output to each other so they can divide the grid load between each other in a sensible way
|
||||
/// </summary>
|
||||
public readonly float ReactorMaxOutput;
|
||||
|
||||
public PowerRange(float min, float max) : this(min, max, 0.0f)
|
||||
{
|
||||
}
|
||||
|
||||
public PowerRange(float min, float max, float reactorMaxOutput)
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(max >= min);
|
||||
System.Diagnostics.Debug.Assert(min >= 0);
|
||||
System.Diagnostics.Debug.Assert(max >= 0);
|
||||
Min = min;
|
||||
Max = max;
|
||||
ReactorMaxOutput = reactorMaxOutput;
|
||||
}
|
||||
|
||||
public static PowerRange operator +(PowerRange a, PowerRange b)
|
||||
{
|
||||
return new PowerRange(a.Min + b.Min, a.Max + b.Max, a.ReactorMaxOutput + b.ReactorMaxOutput);
|
||||
}
|
||||
public static PowerRange operator -(PowerRange a, PowerRange b)
|
||||
{
|
||||
return new PowerRange(a.Min - b.Min, a.Max - b.Max, a.ReactorMaxOutput - b.ReactorMaxOutput);
|
||||
}
|
||||
}
|
||||
|
||||
partial class Powered : ItemComponent
|
||||
{
|
||||
private static float updateTimer;
|
||||
protected static float UpdateInterval = 0.2f;
|
||||
//TODO: test sparser update intervals?
|
||||
protected const float UpdateInterval = (float)Timing.Step;
|
||||
|
||||
/// <summary>
|
||||
/// List of all powered ItemComponents
|
||||
@@ -22,10 +68,9 @@ namespace Barotrauma.Items.Components
|
||||
get { return poweredList; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Items that have already received the "probe signal" that's used to distribute power and load across the grid
|
||||
/// </summary>
|
||||
protected static HashSet<PowerTransfer> lastPowerProbeRecipients = new HashSet<PowerTransfer>();
|
||||
public static readonly List<Connection> ChangedConnections = new List<Connection>();
|
||||
|
||||
public readonly static Dictionary<int, GridInfo> Grids = new Dictionary<int, GridInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// The amount of power currently consumed by the item. Negative values mean that the item is providing power to connected items
|
||||
@@ -49,7 +94,9 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
protected Connection powerIn, powerOut;
|
||||
|
||||
[Editable, Serialize(0.5f, true, description: "The minimum voltage required for the device to function. " +
|
||||
protected virtual PowerPriority Priority { get { return PowerPriority.Default; } }
|
||||
|
||||
[Editable, Serialize(0.5f, IsPropertySaveable.Yes, description: "The minimum voltage required for the device to function. " +
|
||||
"The voltage is calculated as power / powerconsumption, meaning that a device " +
|
||||
"with a power consumption of 1000 kW would need at least 500 kW of power to work if the minimum voltage is set to 0.5.")]
|
||||
public float MinVoltage
|
||||
@@ -58,14 +105,14 @@ namespace Barotrauma.Items.Components
|
||||
set { minVoltage = value; }
|
||||
}
|
||||
|
||||
[Editable, Serialize(0.0f, true, description: "How much power the device draws (or attempts to draw) from the electrical grid when active.")]
|
||||
[Editable, Serialize(0.0f, IsPropertySaveable.Yes, description: "How much power the device draws (or attempts to draw) from the electrical grid when active.")]
|
||||
public float PowerConsumption
|
||||
{
|
||||
get { return powerConsumption; }
|
||||
set { powerConsumption = value; }
|
||||
}
|
||||
|
||||
[Serialize(false, true, description: "Is the device currently active. Inactive devices don't consume power.")]
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Is the device currently active. Inactive devices don't consume power.")]
|
||||
public override bool IsActive
|
||||
{
|
||||
get { return base.IsActive; }
|
||||
@@ -79,55 +126,66 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(0.0f, true, description: "The current power consumption of the device. Intended to be used by StatusEffect conditionals (setting the value from XML is not recommended).")]
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes, description: "The current power consumption of the device. Intended to be used by StatusEffect conditionals (setting the value from XML is not recommended).")]
|
||||
public float CurrPowerConsumption
|
||||
{
|
||||
get {return currPowerConsumption; }
|
||||
set { currPowerConsumption = value; }
|
||||
}
|
||||
|
||||
[Serialize(0.0f, true, description: "The current voltage of the item (calculated as power consumption / available power). Intended to be used by StatusEffect conditionals (setting the value from XML is not recommended).")]
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes, description: "The current voltage of the item (calculated as power consumption / available power). Intended to be used by StatusEffect conditionals (setting the value from XML is not recommended).")]
|
||||
public float Voltage
|
||||
{
|
||||
get { return voltage; }
|
||||
set { voltage = Math.Max(0.0f, value); }
|
||||
get
|
||||
{
|
||||
if (powerIn != null)
|
||||
{
|
||||
if (powerIn?.Grid != null) { return powerIn.Grid.Voltage; }
|
||||
}
|
||||
else if (powerOut != null)
|
||||
{
|
||||
if (powerOut?.Grid != null) { return powerOut.Grid.Voltage; }
|
||||
}
|
||||
return voltage;
|
||||
}
|
||||
set
|
||||
{
|
||||
voltage = Math.Max(0.0f, value);
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize(true, true, description: "Can the item be damaged by electomagnetic pulses.")]
|
||||
[Editable, Serialize(true, IsPropertySaveable.Yes, description: "Can the item be damaged by electomagnetic pulses.")]
|
||||
public bool VulnerableToEMP
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Powered(Item item, XElement element)
|
||||
public Powered(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
poweredList.Add(this);
|
||||
InitProjectSpecific(element);
|
||||
}
|
||||
|
||||
partial void InitProjectSpecific(XElement element);
|
||||
partial void InitProjectSpecific(ContentXElement element);
|
||||
|
||||
protected void UpdateOnActiveEffects(float deltaTime)
|
||||
{
|
||||
if (currPowerConsumption <= 0.0f)
|
||||
if (currPowerConsumption <= 0.0f && PowerConsumption <= 0.0f)
|
||||
{
|
||||
//if the item consumes no power, ignore the voltage requirement and
|
||||
//apply OnActive statuseffects as long as this component is active
|
||||
if (powerConsumption <= 0.0f)
|
||||
{
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
}
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (voltage > minVoltage)
|
||||
if (Voltage > minVoltage)
|
||||
{
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
}
|
||||
#if CLIENT
|
||||
if (voltage > minVoltage)
|
||||
if (Voltage > minVoltage)
|
||||
{
|
||||
if (!powerOnSoundPlayed && powerOnSound != null)
|
||||
{
|
||||
@@ -135,7 +193,7 @@ namespace Barotrauma.Items.Components
|
||||
powerOnSoundPlayed = true;
|
||||
}
|
||||
}
|
||||
else if (voltage < 0.1f)
|
||||
else if (Voltage < 0.1f)
|
||||
{
|
||||
powerOnSoundPlayed = false;
|
||||
}
|
||||
@@ -144,7 +202,6 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
currPowerConsumption = powerConsumption;
|
||||
UpdateOnActiveEffects(deltaTime);
|
||||
}
|
||||
|
||||
@@ -163,6 +220,11 @@ namespace Barotrauma.Items.Components
|
||||
else if (c.Name == "power_out")
|
||||
{
|
||||
powerOut = c;
|
||||
// Connection takes the lowest priority
|
||||
if (Priority > powerOut.Priority)
|
||||
{
|
||||
powerOut.Priority = Priority;
|
||||
}
|
||||
}
|
||||
else if (c.Name == "power")
|
||||
{
|
||||
@@ -182,6 +244,11 @@ namespace Barotrauma.Items.Components
|
||||
#endif
|
||||
}
|
||||
powerOut = c;
|
||||
// Connection takes the lowest priority
|
||||
if (Priority > powerOut.Priority)
|
||||
{
|
||||
powerOut.Priority = Priority;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -199,104 +266,393 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ReceivePowerProbeSignal(Connection connection, Item source, float power) { }
|
||||
|
||||
/// <summary>
|
||||
/// Allocate electrical devices into their grids based on connections
|
||||
/// </summary>
|
||||
/// <param name="useCache">Use previous grids and change in connections</param>
|
||||
public static void UpdateGrids(bool useCache = true)
|
||||
{
|
||||
//don't use cache if there are no existing grids
|
||||
if (Grids.Count > 0 && useCache)
|
||||
{
|
||||
//delete all grids that were affected
|
||||
foreach (Connection c in ChangedConnections)
|
||||
{
|
||||
if (c.Grid != null)
|
||||
{
|
||||
Grids.Remove(c.Grid.ID);
|
||||
c.Grid = null;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Connection c in ChangedConnections)
|
||||
{
|
||||
//Make sure the connection grid hasn't been resolved by another connection update
|
||||
//Ensure the connection has other connections
|
||||
if (c.Grid == null && c.Recipients.Count > 0 && c.Item.Condition > 0.0f)
|
||||
{
|
||||
GridInfo grid = PropagateGrid(c);
|
||||
Grids[grid.ID] = grid;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Clear all grid IDs from connections
|
||||
foreach (Powered powered in poweredList)
|
||||
{
|
||||
//Only check devices with connectors
|
||||
if (powered.powerIn != null)
|
||||
{
|
||||
powered.powerIn.Grid = null;
|
||||
}
|
||||
if (powered.powerOut != null)
|
||||
{
|
||||
powered.powerOut.Grid = null;
|
||||
}
|
||||
}
|
||||
|
||||
Grids.Clear();
|
||||
|
||||
foreach (Powered powered in poweredList)
|
||||
{
|
||||
//Probe through all connections that don't have a gridID
|
||||
if (powered.powerIn != null && powered.powerIn.Grid == null && powered.powerIn != powered.powerOut && powered.Item.Condition > 0.0f)
|
||||
{
|
||||
// Only create grids for networks with more than 1 device
|
||||
if (powered.powerIn.Recipients.Count > 0)
|
||||
{
|
||||
GridInfo grid = PropagateGrid(powered.powerIn);
|
||||
Grids[grid.ID] = grid;
|
||||
}
|
||||
}
|
||||
|
||||
if (powered.powerOut != null && powered.powerOut.Grid == null && powered.Item.Condition > 0.0f)
|
||||
{
|
||||
//Only create grids for networks with more than 1 device
|
||||
if (powered.powerOut.Recipients.Count > 0)
|
||||
{
|
||||
GridInfo grid = PropagateGrid(powered.powerOut);
|
||||
Grids[grid.ID] = grid;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Clear changed connections after each update
|
||||
ChangedConnections.Clear();
|
||||
}
|
||||
|
||||
private static GridInfo PropagateGrid(Connection conn)
|
||||
{
|
||||
//Generate unique Key
|
||||
int id = Rand.Int(int.MaxValue, Rand.RandSync.Unsynced);
|
||||
while (Grids.ContainsKey(id))
|
||||
{
|
||||
id = Rand.Int(int.MaxValue, Rand.RandSync.Unsynced);
|
||||
}
|
||||
|
||||
return PropagateGrid(conn, id);
|
||||
}
|
||||
|
||||
private static GridInfo PropagateGrid(Connection conn, int gridID)
|
||||
{
|
||||
Stack<Connection> probeStack = new Stack<Connection>();
|
||||
|
||||
GridInfo grid = new GridInfo(gridID);
|
||||
|
||||
probeStack.Push(conn);
|
||||
|
||||
//Non recursive approach to traversing connection tree
|
||||
while (probeStack.Count > 0)
|
||||
{
|
||||
Connection c = probeStack.Pop();
|
||||
c.Grid = grid;
|
||||
grid.AddConnection(c);
|
||||
|
||||
//Add on recipients
|
||||
foreach (Connection otherC in c.Recipients)
|
||||
{
|
||||
//Only add valid connections
|
||||
if (otherC.Grid != grid && (otherC.Grid == null || !Grids.ContainsKey(otherC.Grid.ID)) && ValidPowerConnection(c, otherC))
|
||||
{
|
||||
if (otherC.Item.Condition <= 0.0f)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
otherC.Grid = grid; //Assigning ID early prevents unncessary adding to stack
|
||||
probeStack.Push(otherC);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return grid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the power calculations of all devices and grids
|
||||
/// Updates grids in the order of
|
||||
/// ConnCurrConsumption - Get load of device/ flag it as an outputting connection
|
||||
/// -- If outputting power --
|
||||
/// MinMaxPower - Minimum and Maximum power output of the connection for devices to coordinate
|
||||
/// ConnPowerOut - Final power output based on the sum of the MinMaxPower
|
||||
/// -- Finally --
|
||||
/// GridResolved - Indicate that a connection's grid has been finished being calculated
|
||||
///
|
||||
/// Power outputting devices are calculated in stages based on their priority
|
||||
/// Reactors will output first, followed by relays then batteries.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="deltaTime"></param>
|
||||
public static void UpdatePower(float deltaTime)
|
||||
{
|
||||
//Don't update the power if the round is ending
|
||||
if (GameMain.GameSession != null && GameMain.GameSession.RoundEnding)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//Only update the power at the given update interval
|
||||
/*
|
||||
//Not use currently as update interval of 1/60
|
||||
if (updateTimer > 0.0f)
|
||||
{
|
||||
updateTimer -= deltaTime;
|
||||
return;
|
||||
}
|
||||
updateTimer = UpdateInterval;
|
||||
*/
|
||||
|
||||
//reset power first
|
||||
foreach (Powered powered in poweredList)
|
||||
#if CLIENT
|
||||
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
|
||||
sw.Start();
|
||||
#endif
|
||||
//Ensure all grids are updated correctly and have the correct connections
|
||||
UpdateGrids();
|
||||
|
||||
#if CLIENT
|
||||
sw.Stop();
|
||||
GameMain.PerformanceCounter.AddElapsedTicks("GridUpdate", sw.ElapsedTicks);
|
||||
sw.Restart();
|
||||
#endif
|
||||
|
||||
//Reset all grids
|
||||
foreach (GridInfo grid in Grids.Values)
|
||||
{
|
||||
if (powered is PowerTransfer pt)
|
||||
{
|
||||
powered.CurrPowerConsumption = 0.0f;
|
||||
pt.PowerLoad = 0.0f;
|
||||
if (pt is RelayComponent relay)
|
||||
{
|
||||
relay.DisplayLoad = 0.0f;
|
||||
}
|
||||
}
|
||||
//only reset voltage if the item has a power connector
|
||||
//(other items, such as handheld devices, get power through other means and shouldn't be updated here)
|
||||
if (powered.powerIn != null || powered.powerOut != null) { powered.voltage = 0.0f; }
|
||||
//Wipe priority groups as connections can change to not be outputting -- Can be improved caching wise --
|
||||
grid.PowerSourceGroups.Clear();
|
||||
grid.Power = 0;
|
||||
grid.Load = 0;
|
||||
}
|
||||
|
||||
//go through all the devices that are consuming/providing power
|
||||
//and send out a "probe signal" which the PowerTransfer components use to add up the grid power/load
|
||||
//Determine if devices are adding a load or providing power, also resolve solo nodes
|
||||
foreach (Powered powered in poweredList)
|
||||
{
|
||||
if (powered is PowerTransfer pt)
|
||||
{
|
||||
if (pt.ExtraLoad > 0.0f)
|
||||
{
|
||||
lastPowerProbeRecipients.Clear();
|
||||
powered.powerIn?.SendPowerProbeSignal(powered.item, -pt.ExtraLoad);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (powered.currPowerConsumption > 0.0f)
|
||||
{
|
||||
//consuming power
|
||||
lastPowerProbeRecipients.Clear();
|
||||
powered.powerIn?.SendPowerProbeSignal(powered.item, -powered.currPowerConsumption);
|
||||
}
|
||||
}
|
||||
foreach (Powered powered in poweredList)
|
||||
{
|
||||
if (powered is PowerTransfer) { continue; }
|
||||
else if (powered.currPowerConsumption < 0.0f)
|
||||
{
|
||||
//providing power
|
||||
lastPowerProbeRecipients.Clear();
|
||||
powered.powerOut?.SendPowerProbeSignal(powered.item, -powered.currPowerConsumption);
|
||||
}
|
||||
if (powered is PowerContainer pc)
|
||||
{
|
||||
if (pc.CurrPowerOutput <= 0.0f || pc.item.Condition <= 0.0f) { continue; }
|
||||
//providing power
|
||||
lastPowerProbeRecipients.Clear();
|
||||
powered.powerOut?.SendPowerProbeSignal(powered.item, pc.CurrPowerOutput);
|
||||
}
|
||||
}
|
||||
//go through powered items and calculate their current voltage
|
||||
foreach (Powered powered in poweredList)
|
||||
{
|
||||
if (powered is PowerTransfer pt1 || (pt1 = powered.Item.GetComponent<PowerTransfer>()) != null)
|
||||
{
|
||||
powered.voltage = -pt1.CurrPowerConsumption / Math.Max(pt1.PowerLoad, 1.0f);
|
||||
continue;
|
||||
}
|
||||
if ((powered.powerConsumption <= 0.0f || (powered.Item.GetComponent<Repairable>() is Repairable repairable && repairable.IsTinkering && repairable.TinkeringPowersDevices)) && !(powered is PowerContainer))
|
||||
{
|
||||
powered.voltage = 1.0f;
|
||||
continue;
|
||||
}
|
||||
if (powered.powerIn == null) { continue; }
|
||||
//Make voltage decay to ensure the device powers down.
|
||||
//This only effects devices with no power input (whose voltage is set by other means, e.g. status effects from a contained battery)
|
||||
//or devices that have been disconnected from the power grid - other devices use the voltage of the grid instead.
|
||||
powered.Voltage -= deltaTime;
|
||||
|
||||
foreach (Connection powerSource in powered.powerIn.Recipients)
|
||||
//Handle the device if it's got a power connection
|
||||
if (powered.powerIn != null && powered.powerOut != powered.powerIn)
|
||||
{
|
||||
if (!powerSource.IsPower || !powerSource.IsOutput) { continue; }
|
||||
var pt = powerSource.Item.GetComponent<PowerTransfer>();
|
||||
if (pt != null)
|
||||
//Get the new load for the connection
|
||||
float currLoad;
|
||||
if (powered.Item.GetComponent<Repairable>() is Repairable repairable && repairable.IsTinkering && repairable.TinkeringPowersDevices && !(powered is PowerContainer))
|
||||
{
|
||||
float voltage = -pt.CurrPowerConsumption / Math.Max(pt.PowerLoad, 1.0f);
|
||||
powered.voltage = Math.Max(powered.voltage, voltage);
|
||||
continue;
|
||||
currLoad = 0.0f;
|
||||
}
|
||||
var pc = powerSource.Item.GetComponent<PowerContainer>();
|
||||
if (pc != null && pc.item.Condition > 0.0f)
|
||||
else
|
||||
{
|
||||
float voltage = pc.CurrPowerOutput / Math.Max(powered.CurrPowerConsumption, 1.0f);
|
||||
powered.voltage += voltage;
|
||||
currLoad = powered.GetCurrentPowerConsumption(powered.powerIn);
|
||||
}
|
||||
|
||||
//If its a load update its grid load
|
||||
if (currLoad >= 0)
|
||||
{
|
||||
powered.CurrPowerConsumption = currLoad;
|
||||
if (powered.powerIn.Grid != null)
|
||||
{
|
||||
powered.powerIn.Grid.Load += currLoad;
|
||||
}
|
||||
}
|
||||
else if (powered.powerIn.Grid != null)
|
||||
{
|
||||
//If connected to a grid add as a source to be processed
|
||||
powered.powerIn.Grid.AddSrc(powered.powerIn);
|
||||
}
|
||||
else
|
||||
{
|
||||
powered.CurrPowerConsumption = -powered.GetConnectionPowerOut(powered.powerIn, 0, powered.MinMaxPowerOut(powered.powerIn, 0), 0);
|
||||
powered.GridResolved(powered.powerIn);
|
||||
}
|
||||
}
|
||||
|
||||
//Handle the device power depending on if its powerout
|
||||
if (powered.powerOut != null)
|
||||
{
|
||||
//Get the connection's load
|
||||
float currLoad = powered.GetCurrentPowerConsumption(powered.powerOut);
|
||||
|
||||
//Update the device's output load to the correct variable
|
||||
if (powered is PowerTransfer pt)
|
||||
{
|
||||
pt.PowerLoad = currLoad;
|
||||
}
|
||||
else if (powered is PowerContainer pc)
|
||||
{
|
||||
// PowerContainer handle its own output value
|
||||
}
|
||||
else
|
||||
{
|
||||
powered.CurrPowerConsumption = currLoad;
|
||||
}
|
||||
|
||||
if (currLoad >= 0)
|
||||
{
|
||||
//Add to the grid load if possible
|
||||
if (powered.powerOut.Grid != null)
|
||||
{
|
||||
powered.powerOut.Grid.Load += currLoad;
|
||||
}
|
||||
}
|
||||
else if (powered.powerOut.Grid != null)
|
||||
{
|
||||
//Add connection as a source to be processed
|
||||
powered.powerOut.Grid.AddSrc(powered.powerOut);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Perform power calculations for the singular connection
|
||||
float loadOut = -powered.GetConnectionPowerOut(powered.powerOut, 0, powered.MinMaxPowerOut(powered.powerOut, 0), 0);
|
||||
if (powered is PowerTransfer pt2)
|
||||
{
|
||||
pt2.PowerLoad = loadOut;
|
||||
}
|
||||
else if (powered is PowerContainer pc)
|
||||
{
|
||||
//PowerContainer handles its own output value
|
||||
}
|
||||
else
|
||||
{
|
||||
powered.CurrPowerConsumption = loadOut;
|
||||
}
|
||||
|
||||
//Indicate grid is resolved as it was the only device
|
||||
powered.GridResolved(powered.powerOut);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Iterate through all grids to determine the power on the grid
|
||||
foreach (GridInfo grid in Grids.Values)
|
||||
{
|
||||
//Iterate through the priority src groups lowest first
|
||||
foreach (PowerSourceGroup scrGroup in grid.PowerSourceGroups.Values)
|
||||
{
|
||||
scrGroup.MinMaxPower = PowerRange.Zero;
|
||||
|
||||
//Iterate through all connections in the group to get their minmax power and sum them
|
||||
foreach (Connection c in scrGroup.Connections)
|
||||
{
|
||||
Powered device = c.Item.GetComponent<Powered>();
|
||||
scrGroup.MinMaxPower += device.MinMaxPowerOut(c, grid.Load);
|
||||
}
|
||||
|
||||
//Iterate through all connections to get their final power out provided the min max information
|
||||
float addedPower = 0;
|
||||
foreach (Connection c in scrGroup.Connections)
|
||||
{
|
||||
Powered device = c.Item.GetComponent<Powered>();
|
||||
addedPower += device.GetConnectionPowerOut(c, grid.Power, scrGroup.MinMaxPower, grid.Load);
|
||||
}
|
||||
|
||||
//Add the power to the grid
|
||||
grid.Power += addedPower;
|
||||
}
|
||||
|
||||
//Calculate Grid voltage, limit between 0 - 1000
|
||||
float newVoltage = MathHelper.Min(grid.Power / MathHelper.Max(grid.Load, 1E-10f), 1000);
|
||||
if (float.IsNegative(newVoltage))
|
||||
{
|
||||
newVoltage = 0.0f;
|
||||
}
|
||||
|
||||
grid.Voltage = newVoltage;
|
||||
|
||||
//Iterate through all connections on that grid and run their gridResolved function
|
||||
foreach (Connection con in grid.Connections)
|
||||
{
|
||||
Powered device = con.Item.GetComponent<Powered>();
|
||||
device?.GridResolved(con);
|
||||
}
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
sw.Stop();
|
||||
GameMain.PerformanceCounter.AddElapsedTicks("PowerUpdate", sw.ElapsedTicks);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Current power consumption of the device (or amount of generated power if negative)
|
||||
/// </summary>
|
||||
/// <param name="connection">Connection to calculate power consumption for.</param>
|
||||
public virtual float GetCurrentPowerConsumption(Connection connection = null)
|
||||
{
|
||||
// If a handheld device there is no consumption
|
||||
if (powerIn == null && powerOut == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Add extraload for PowerTransfer devices
|
||||
if (this is PowerTransfer pt)
|
||||
{
|
||||
return PowerConsumption + pt.ExtraLoad;
|
||||
}
|
||||
else if (connection != this.powerIn || !IsActive)
|
||||
{
|
||||
//If not the power in connection or is inactive there is no draw
|
||||
return 0;
|
||||
}
|
||||
|
||||
//Otherwise return the max powerconsumption of the device
|
||||
return PowerConsumption;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Minimum and maximum power the connection can provide
|
||||
/// </summary>
|
||||
/// <param name="conn">Connection being queried about its power capabilities</param>
|
||||
/// <param name="load">Load of the connected grid</param>
|
||||
public virtual PowerRange MinMaxPowerOut(Connection conn, float load = 0)
|
||||
{
|
||||
return PowerRange.Zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalize how much power the device will be outputting to the connection
|
||||
/// </summary>
|
||||
/// <param name="conn">Connection being queried</param>
|
||||
/// <param name="power">Current grid power</param>
|
||||
/// <param name="load">Current load on the grid</param>
|
||||
/// <returns>Power pushed to the grid</returns>
|
||||
public virtual float GetConnectionPowerOut(Connection conn, float power, PowerRange minMaxPower, float load)
|
||||
{
|
||||
return conn == powerOut ? MathHelper.Max(-CurrPowerConsumption, 0) : 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Can be overridden to perform updates for the device after the connected grid has resolved its power calculations, i.e. storing voltage for later updates
|
||||
/// </summary>
|
||||
public virtual void GridResolved(Connection conn) { }
|
||||
|
||||
public static bool ValidPowerConnection(Connection conn1, Connection conn2)
|
||||
{
|
||||
return conn1.IsPower && conn2.IsPower && (conn1.Item.HasTag("junctionbox") || conn2.Item.HasTag("junctionbox") || conn1.Item.HasTag("dock") || conn2.Item.HasTag("dock") || conn1.IsOutput != conn2.IsOutput);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -314,7 +670,6 @@ namespace Barotrauma.Items.Components
|
||||
if (!recipient.IsPower || !recipient.IsOutput) { continue; }
|
||||
var battery = recipient.Item?.GetComponent<PowerContainer>();
|
||||
if (battery == null) { continue; }
|
||||
|
||||
float maxOutputPerFrame = battery.MaxOutPut / 60.0f;
|
||||
float framesPerMinute = 3600.0f;
|
||||
availablePower += Math.Min(battery.Charge * framesPerMinute, maxOutputPerFrame);
|
||||
@@ -323,10 +678,119 @@ namespace Barotrauma.Items.Components
|
||||
return availablePower;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Efficient method to retrieve the batteries connected to the device
|
||||
/// </summary>
|
||||
/// <returns>All connected PowerContainers</returns>
|
||||
protected List<PowerContainer> GetConnectedBatteries(bool outputOnly = true)
|
||||
{
|
||||
List<PowerContainer> batteries = new List<PowerContainer>();
|
||||
GridInfo supplyingGrid = null;
|
||||
|
||||
//Determine supplying grid, prefer PowerIn connection
|
||||
if (powerIn != null)
|
||||
{
|
||||
if (powerIn.Grid != null)
|
||||
{
|
||||
supplyingGrid = powerIn.Grid;
|
||||
}
|
||||
}
|
||||
else if (powerOut != null)
|
||||
{
|
||||
if (powerOut.Grid != null)
|
||||
{
|
||||
supplyingGrid = powerOut.Grid;
|
||||
}
|
||||
}
|
||||
|
||||
if (supplyingGrid != null)
|
||||
{
|
||||
//Iterate through all connections to fine powerContainers
|
||||
foreach (Connection c in supplyingGrid.Connections)
|
||||
{
|
||||
PowerContainer pc = c.Item.GetComponent<PowerContainer>();
|
||||
if (pc != null && (!outputOnly || pc.powerOut == c))
|
||||
{
|
||||
batteries.Add(pc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return batteries;
|
||||
}
|
||||
|
||||
protected override void RemoveComponentSpecific()
|
||||
{
|
||||
//Flag power connections to be updated
|
||||
if (item.Connections != null)
|
||||
{
|
||||
foreach (Connection c in item.Connections)
|
||||
{
|
||||
if (c.IsPower && c.Grid != null)
|
||||
{
|
||||
ChangedConnections.Add(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
base.RemoveComponentSpecific();
|
||||
poweredList.Remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
partial class GridInfo
|
||||
{
|
||||
public readonly int ID;
|
||||
public float Voltage = 0;
|
||||
public float Load = 0;
|
||||
public float Power = 0;
|
||||
|
||||
public readonly List<Connection> Connections = new List<Connection>();
|
||||
public readonly SortedList<PowerPriority, PowerSourceGroup> PowerSourceGroups = new SortedList<PowerPriority, PowerSourceGroup>();
|
||||
|
||||
public GridInfo(int id)
|
||||
{
|
||||
ID = id;
|
||||
}
|
||||
|
||||
public void RemoveConnection(Connection c)
|
||||
{
|
||||
Connections.Remove(c);
|
||||
|
||||
//Remove the grid if it has no devices
|
||||
if (Connections.Count == 0 && Powered.Grids.ContainsKey(ID))
|
||||
{
|
||||
Powered.Grids.Remove(ID);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddConnection(Connection c)
|
||||
{
|
||||
Connections.Add(c);
|
||||
}
|
||||
|
||||
public void AddSrc(Connection c)
|
||||
{
|
||||
if (PowerSourceGroups.ContainsKey(c.Priority))
|
||||
{
|
||||
PowerSourceGroups[c.Priority].Connections.Add(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
PowerSourceGroup group = new PowerSourceGroup();
|
||||
group.Connections.Add(c);
|
||||
PowerSourceGroups[c.Priority] = group;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partial class PowerSourceGroup
|
||||
{
|
||||
public PowerRange MinMaxPower;
|
||||
public readonly List<Connection> Connections = new List<Connection>();
|
||||
|
||||
public PowerSourceGroup()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Voronoi2;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
@@ -49,9 +48,8 @@ namespace Barotrauma.Items.Components
|
||||
//continuous collision detection is used while the projectile is moving faster than this
|
||||
const float ContinuousCollisionThreshold = 5.0f;
|
||||
|
||||
//a duration during which the projectile won't drop from the body it's stuck to
|
||||
private const float PersistentStickJointDuration = 1.0f;
|
||||
private PrismaticJoint stickJoint;
|
||||
private Joint stickJoint;
|
||||
private Vector2 jointAxis;
|
||||
|
||||
public Attack Attack { get; private set; }
|
||||
|
||||
@@ -86,15 +84,13 @@ namespace Barotrauma.Items.Components
|
||||
get { return hits; }
|
||||
}
|
||||
|
||||
private float persistentStickJointTimer;
|
||||
|
||||
[Serialize(10.0f, false, description: "The impulse applied to the physics body of the item when it's launched. Higher values make the projectile faster.")]
|
||||
[Serialize(10.0f, IsPropertySaveable.No, description: "The impulse applied to the physics body of the item when it's launched. Higher values make the projectile faster.")]
|
||||
public float LaunchImpulse { get; set; }
|
||||
|
||||
[Serialize(0.0f, false, description: "The random percentage modifier used to add variance to the launch impulse.")]
|
||||
[Serialize(0.0f, IsPropertySaveable.No, description: "The random percentage modifier used to add variance to the launch impulse.")]
|
||||
public float ImpulseSpread { get; set; }
|
||||
|
||||
[Serialize(0.0f, false, description: "The rotation of the item relative to the rotation of the weapon when launched (in degrees).")]
|
||||
[Serialize(0.0f, IsPropertySaveable.No, description: "The rotation of the item relative to the rotation of the weapon when launched (in degrees).")]
|
||||
|
||||
public float LaunchRotation
|
||||
{
|
||||
@@ -108,7 +104,7 @@ namespace Barotrauma.Items.Components
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "When set to true, the item can stick to any target it hits.")]
|
||||
[Serialize(false, IsPropertySaveable.No, description: "When set to true, the item can stick to any target it hits.")]
|
||||
//backwards compatibility, can stick to anything
|
||||
public bool DoesStick
|
||||
{
|
||||
@@ -116,50 +112,50 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "When set to true, the item won't fall of a target it's stuck to unless removed.")]
|
||||
public bool StickPermanently
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "Can the item stick to the character it hits.")]
|
||||
[Serialize(false, IsPropertySaveable.No, description: "Can the item stick to the character it hits.")]
|
||||
public bool StickToCharacters
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "Can the item stick to the structure it hits.")]
|
||||
[Serialize(false, IsPropertySaveable.No, description: "Can the item stick to the structure it hits.")]
|
||||
public bool StickToStructures
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "Can the item stick to the item it hits.")]
|
||||
[Serialize(false, IsPropertySaveable.No, description: "Can the item stick to the item it hits.")]
|
||||
public bool StickToItems
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "Can the item stick even to deflective targets.")]
|
||||
[Serialize(false, IsPropertySaveable.No, description: "Can the item stick even to deflective targets.")]
|
||||
public bool StickToDeflective
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "Hitscan projectiles cast a ray forwards and immediately hit whatever the ray hits. "+
|
||||
"It is recommended to use hitscans for very fast-moving projectiles such as bullets, because using extremely fast launch velocities may cause physics glitches.")]
|
||||
[Serialize(false, IsPropertySaveable.No, description: "")]
|
||||
public bool StickToLightTargets
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No, description: "Hitscan projectiles cast a ray forwards and immediately hit whatever the ray hits. "+
|
||||
"It is recommended to use hitscans for very fast-moving projectiles such as bullets, because using extremely fast launch velocities may cause physics glitches.")]
|
||||
public bool Hitscan
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(1, false, description: "How many hitscans should be done when the projectile is launched. "
|
||||
[Serialize(1, IsPropertySaveable.No, description: "How many hitscans should be done when the projectile is launched. "
|
||||
+ "Multiple hitscans can be used to simulate weapons that fire multiple projectiles at the same time" +
|
||||
" without having to actually use multiple projectile items, for example shotguns.")]
|
||||
public int HitScanCount
|
||||
@@ -168,70 +164,97 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(1, false, description: "How many targets the projectile can hit before it stops.")]
|
||||
[Serialize(1, IsPropertySaveable.No, description: "How many targets the projectile can hit before it stops.")]
|
||||
public int MaxTargetsToHit
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "Should the item be deleted when it hits something.")]
|
||||
[Serialize(false, IsPropertySaveable.No, description: "Should the item be deleted when it hits something.")]
|
||||
public bool RemoveOnHit
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(0.0f, false, description: "Random spread applied to the launch angle of the projectile (in degrees).")]
|
||||
[Serialize(0.0f, IsPropertySaveable.No, description: "Random spread applied to the launch angle of the projectile (in degrees).")]
|
||||
public float Spread
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "Override random spread with static spread; hitscan are launched with an equal amount of angle between them. Only applies when firing multiple hitscan.")]
|
||||
[Serialize(false, IsPropertySaveable.No, description: "Override random spread with static spread; hitscan are launched with an equal amount of angle between them. Only applies when firing multiple hitscan.")]
|
||||
public bool StaticSpread
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(true, IsPropertySaveable.No)]
|
||||
public bool FriendlyFire
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
private float deactivationTimer;
|
||||
|
||||
[Serialize(0f, false)]
|
||||
[Serialize(0f, IsPropertySaveable.No)]
|
||||
public float DeactivationTime
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
private float stickTimer;
|
||||
[Serialize(0f, IsPropertySaveable.No)]
|
||||
public float StickDuration
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(-1f, IsPropertySaveable.No)]
|
||||
public float MaxJointTranslation
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
private float maxJointTranslationInSimUnits = -1;
|
||||
|
||||
[Serialize(true, IsPropertySaveable.No)]
|
||||
public bool Prismatic
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Body StickTarget
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public bool IsStuckToTarget
|
||||
{
|
||||
get { return StickTarget != null; }
|
||||
}
|
||||
public bool IsStuckToTarget => StickTarget != null;
|
||||
|
||||
private Category originalCollisionCategories;
|
||||
private Category originalCollisionTargets;
|
||||
|
||||
public Projectile(Item item, XElement element)
|
||||
public Projectile(Item item, ContentXElement element)
|
||||
: base (item, element)
|
||||
{
|
||||
IgnoredBodies = new List<Body>();
|
||||
|
||||
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 + ", Projectile", item);
|
||||
}
|
||||
InitProjSpecific(element);
|
||||
}
|
||||
partial void InitProjSpecific(XElement element);
|
||||
partial void InitProjSpecific(ContentXElement element);
|
||||
|
||||
public override void OnItemLoaded()
|
||||
{
|
||||
@@ -309,7 +332,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
#if SERVER
|
||||
launchRot = rotation;
|
||||
Item.CreateServerEvent(this, new object[] { true }); //true = indicate that this is a launch event
|
||||
Item.CreateServerEvent(this, new EventData(launch: true));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -484,7 +507,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
Entity.Spawner.AddToRemoveQueue(item);
|
||||
Entity.Spawner.AddItemToRemoveQueue(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -653,27 +676,26 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (stickJoint == null) { return; }
|
||||
|
||||
if (persistentStickJointTimer > 0.0f && !StickPermanently)
|
||||
if (StickDuration > 0 && stickTimer > 0)
|
||||
{
|
||||
persistentStickJointTimer -= deltaTime;
|
||||
stickTimer -= deltaTime;
|
||||
return;
|
||||
}
|
||||
|
||||
float absoluteMaxTranslation = 100;
|
||||
// Update the item's transform to make sure it's inside the same sub as the target (or outside)
|
||||
if (StickTarget?.UserData is Limb target && target.Submarine != item.Submarine || Math.Abs(stickJoint.JointTranslation) > 100.0f)
|
||||
if (StickTarget?.UserData is Limb target && target.Submarine != item.Submarine || stickJoint is PrismaticJoint prismaticJoint && Math.Abs(prismaticJoint.JointTranslation) > absoluteMaxTranslation)
|
||||
{
|
||||
item.UpdateTransform();
|
||||
}
|
||||
|
||||
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
if (StickTargetRemoved() ||
|
||||
(!StickPermanently && (stickJoint.JointTranslation < stickJoint.LowerLimit * 0.9f || stickJoint.JointTranslation > stickJoint.UpperLimit * 0.9f)) ||
|
||||
Math.Abs(stickJoint.JointTranslation) > 100.0f) //failsafe unstick if the target is still extremely far
|
||||
if (StickTargetRemoved() || stickJoint is PrismaticJoint pJoint && Math.Abs(pJoint.JointTranslation) > maxJointTranslationInSimUnits)
|
||||
{
|
||||
Unstick();
|
||||
#if SERVER
|
||||
item.CreateServerEvent(this);
|
||||
item.CreateServerEvent(this, new EventData(launch: false));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -697,27 +719,9 @@ namespace Barotrauma.Items.Components
|
||||
return false;
|
||||
}
|
||||
if (hits.Contains(target.Body)) { return false; }
|
||||
if (target.Body.UserData is Submarine sub)
|
||||
if (target.Body.UserData is Submarine)
|
||||
{
|
||||
Vector2 dir = item.body.LinearVelocity.LengthSquared() < 0.001f ?
|
||||
contact.Manifold.LocalNormal : Vector2.Normalize(item.body.LinearVelocity);
|
||||
|
||||
//do a raycast in the sub's coordinate space to see if it hit a structure
|
||||
var wallBody = Submarine.PickBody(
|
||||
item.body.SimPosition - ConvertUnits.ToSimUnits(sub.Position) - dir,
|
||||
item.body.SimPosition - ConvertUnits.ToSimUnits(sub.Position) + dir,
|
||||
collisionCategory: Physics.CollisionWall);
|
||||
if (wallBody?.FixtureList?.First() != null && (wallBody.UserData is Structure || wallBody.UserData is Item) &&
|
||||
//ignore the hit if it's behind the position the item was launched from, and the projectile is travelling in the opposite direction
|
||||
Vector2.Dot(item.body.SimPosition - launchPos, dir) > 0)
|
||||
{
|
||||
target = wallBody.FixtureList.First();
|
||||
if (hits.Contains(target.Body)) { return false; }
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (ShouldIgnoreSubmarineCollision(ref target, contact)) { return false; }
|
||||
}
|
||||
else if (target.Body.UserData is Limb limb)
|
||||
{
|
||||
@@ -727,6 +731,10 @@ namespace Barotrauma.Items.Components
|
||||
limb.body?.ApplyLinearImpulse(item.body.LinearVelocity * item.body.Mass * 0.1f, item.SimPosition);
|
||||
return false;
|
||||
}
|
||||
if (!FriendlyFire && User != null && limb.character.IsFriendly(User))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (target.Body.UserData is Item item)
|
||||
{
|
||||
@@ -757,6 +765,47 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Should the collision with the target submarine be ignored (e.g. did the projectile collide with the wall behind the turret when being launched)
|
||||
/// </summary>
|
||||
/// <param name="target">Fixture the projectile hit</param>
|
||||
/// <param name="contact">Contact between the projectile and the target</param>
|
||||
/// <returns>True if the target isn't a submarine or if the collision happened behind the launch position of the projectile</returns>
|
||||
public bool ShouldIgnoreSubmarineCollision(Fixture target, Contact contact)
|
||||
{
|
||||
return ShouldIgnoreSubmarineCollision(ref target, contact);
|
||||
}
|
||||
|
||||
private bool ShouldIgnoreSubmarineCollision(ref Fixture target, Contact contact)
|
||||
{
|
||||
if (target.Body.UserData is Submarine sub)
|
||||
{
|
||||
Vector2 dir = item.body.LinearVelocity.LengthSquared() < 0.001f ?
|
||||
contact.Manifold.LocalNormal : Vector2.Normalize(item.body.LinearVelocity);
|
||||
|
||||
//do a raycast in the sub's coordinate space to see if it hit a structure
|
||||
var wallBody = Submarine.PickBody(
|
||||
item.body.SimPosition - ConvertUnits.ToSimUnits(sub.Position) - dir,
|
||||
item.body.SimPosition - ConvertUnits.ToSimUnits(sub.Position) + dir,
|
||||
collisionCategory: Physics.CollisionWall);
|
||||
if (wallBody?.FixtureList?.First() != null && (wallBody.UserData is Structure || wallBody.UserData is Item) &&
|
||||
//ignore the hit if it's behind the position the item was launched from, and the projectile is travelling in the opposite direction
|
||||
Vector2.Dot(item.body.SimPosition - launchPos, dir) > 0)
|
||||
{
|
||||
target = wallBody.FixtureList.First();
|
||||
if (hits.Contains(target.Body))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private readonly List<ISerializableEntity> targets = new List<ISerializableEntity>();
|
||||
private Fixture lastTarget;
|
||||
|
||||
@@ -873,8 +922,8 @@ namespace Barotrauma.Items.Components
|
||||
#if SERVER
|
||||
if (GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
GameMain.Server?.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, actionType, this, targetLimb.character.ID, targetLimb, (ushort)0, item.WorldPosition });
|
||||
GameMain.Server?.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnImpact, this, targetLimb.character.ID, targetLimb, (ushort)0, item.WorldPosition });
|
||||
GameMain.Server?.CreateEntityEvent(item, new Item.ApplyStatusEffectEventData(actionType, this, targetLimb.character, targetLimb, null, item.WorldPosition));
|
||||
GameMain.Server?.CreateEntityEvent(item, new Item.ApplyStatusEffectEventData(ActionType.OnImpact, this, targetLimb.character, targetLimb, null, item.WorldPosition));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -885,8 +934,8 @@ namespace Barotrauma.Items.Components
|
||||
#if SERVER
|
||||
if (GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
GameMain.Server?.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, actionType, this, (ushort)0, null, (target.Body.UserData as Entity)?.ID ?? 0, item.WorldPosition });
|
||||
GameMain.Server?.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnImpact, this, (ushort)0, null, (target.Body.UserData as Entity)?.ID ?? 0, item.WorldPosition });
|
||||
GameMain.Server?.CreateEntityEvent(item, new Item.ApplyStatusEffectEventData(actionType, this, null, null, target.Body.UserData as Entity, item.WorldPosition));
|
||||
GameMain.Server?.CreateEntityEvent(item, new Item.ApplyStatusEffectEventData(ActionType.OnImpact, this, null, null, target.Body.UserData as Entity, item.WorldPosition));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -905,14 +954,12 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
item.body.LinearVelocity *= deflectedSpeedMultiplier;
|
||||
}
|
||||
else if ( // When hitting characters the collision normal seems to sometimes point into wrong direction, resulting in a failed attempt to stick
|
||||
//Vector2.Dot(Vector2.Normalize(velocity), collisionNormal) < 0.0f &&
|
||||
hits.Count() >= MaxTargetsToHit &&
|
||||
target.Body.Mass > item.body.Mass * 0.5f &&
|
||||
else if ( stickJoint == null && StickTarget == null &&
|
||||
StickToStructures && target.Body.UserData is Structure ||
|
||||
((StickToLightTargets || target.Body.Mass > item.body.Mass * 0.5f) &&
|
||||
(DoesStick ||
|
||||
(StickToCharacters && (target.Body.UserData is Limb || target.Body.UserData is Character)) ||
|
||||
(StickToStructures && target.Body.UserData is Structure) ||
|
||||
(StickToItems && target.Body.UserData is Item)))
|
||||
(StickToItems && target.Body.UserData is Item))))
|
||||
{
|
||||
Vector2 dir = new Vector2(
|
||||
(float)Math.Cos(item.body.Rotation),
|
||||
@@ -932,7 +979,7 @@ namespace Barotrauma.Items.Components
|
||||
#if SERVER
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
item.CreateServerEvent(this);
|
||||
item.CreateServerEvent(this, new EventData(launch: false));
|
||||
}
|
||||
#endif
|
||||
item.body.LinearVelocity *= speedMultiplier;
|
||||
@@ -961,7 +1008,7 @@ namespace Barotrauma.Items.Components
|
||||
removePending = true;
|
||||
item.HiddenInGame = true;
|
||||
item.body.FarseerBody.Enabled = false;
|
||||
Entity.Spawner?.AddToRemoveQueue(item);
|
||||
Entity.Spawner?.AddItemToRemoveQueue(item);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -994,30 +1041,39 @@ namespace Barotrauma.Items.Components
|
||||
private void StickToTarget(Body targetBody, Vector2 axis)
|
||||
{
|
||||
if (stickJoint != null) { return; }
|
||||
|
||||
stickJoint = new PrismaticJoint(targetBody, item.body.FarseerBody, item.body.SimPosition, axis, true)
|
||||
jointAxis = axis;
|
||||
item.body.ResetDynamics();
|
||||
if (Prismatic)
|
||||
{
|
||||
MotorEnabled = true,
|
||||
MaxMotorForce = 30.0f,
|
||||
LimitEnabled = true,
|
||||
Breakpoint = 1000.0f
|
||||
};
|
||||
stickJoint = new PrismaticJoint(targetBody, item.body.FarseerBody, item.body.SimPosition, axis, useWorldCoordinates: true)
|
||||
{
|
||||
MotorEnabled = true,
|
||||
MaxMotorForce = 30.0f,
|
||||
LimitEnabled = true,
|
||||
Breakpoint = 1000.0f
|
||||
};
|
||||
|
||||
if (StickPermanently)
|
||||
{
|
||||
stickJoint.LowerLimit = stickJoint.UpperLimit = 0.0f;
|
||||
item.body.ResetDynamics();
|
||||
if (maxJointTranslationInSimUnits == -1)
|
||||
{
|
||||
if (item.Sprite != null && MaxJointTranslation < 0)
|
||||
{
|
||||
MaxJointTranslation = item.Sprite.size.X / 2 * item.Scale;
|
||||
}
|
||||
MaxJointTranslation = Math.Min(MaxJointTranslation, 1000);
|
||||
maxJointTranslationInSimUnits = ConvertUnits.ToSimUnits(MaxJointTranslation);
|
||||
}
|
||||
}
|
||||
else if (item.Sprite != null)
|
||||
else
|
||||
{
|
||||
stickJoint.LowerLimit = ConvertUnits.ToSimUnits(item.Sprite.size.X * -0.3f * item.Scale);
|
||||
stickJoint.UpperLimit = ConvertUnits.ToSimUnits(item.Sprite.size.X * 0.3f * item.Scale);
|
||||
stickJoint = new WeldJoint(targetBody, item.body.FarseerBody, item.body.SimPosition, item.body.SimPosition, useWorldCoordinates: true)
|
||||
{
|
||||
FrequencyHz = 10.0f,
|
||||
DampingRatio = 0.5f
|
||||
};
|
||||
}
|
||||
|
||||
persistentStickJointTimer = PersistentStickJointDuration;
|
||||
stickTimer = StickDuration;
|
||||
StickTarget = targetBody;
|
||||
GameMain.World.Add(stickJoint);
|
||||
|
||||
IsActive = true;
|
||||
if (targetBody.UserData is Limb limb)
|
||||
{
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private int qualityLevel;
|
||||
|
||||
[Editable, Serialize(0, true)]
|
||||
[Editable, Serialize(0, IsPropertySaveable.Yes)]
|
||||
public int QualityLevel
|
||||
{
|
||||
get { return qualityLevel; }
|
||||
@@ -65,7 +65,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public Quality(Item item, XElement element) : base(item, element)
|
||||
public Quality(Item item, ContentXElement element) : base(item, element)
|
||||
{
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
@@ -77,7 +77,7 @@ namespace Barotrauma.Items.Components
|
||||
string statTypeString = subElement.GetAttributeString("stattype", "");
|
||||
if (!Enum.TryParse(statTypeString, true, out StatType statType))
|
||||
{
|
||||
DebugConsole.ThrowError("Invalid stat type type \"" + statTypeString + "\" in item (" + item.prefab.Identifier + ")");
|
||||
DebugConsole.ThrowError("Invalid stat type type \"" + statTypeString + "\" in item (" + ((MapEntity)item).Prefab.Identifier + ")");
|
||||
}
|
||||
float statValue = subElement.GetAttributeFloat("value", 0f);
|
||||
statValues.TryAdd(statType, statValue);
|
||||
|
||||
@@ -5,21 +5,21 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class RemoteController : ItemComponent
|
||||
{
|
||||
[Serialize("", false, description: "Tag or identifier of the item that should be controlled.")]
|
||||
[Serialize("", IsPropertySaveable.No, description: "Tag or identifier of the item that should be controlled.")]
|
||||
public string Target
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(false, false)]
|
||||
[Serialize(false, IsPropertySaveable.No)]
|
||||
public bool OnlyInOwnSub
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(10000.0f, false)]
|
||||
[Serialize(10000.0f, IsPropertySaveable.No)]
|
||||
public float Range
|
||||
{
|
||||
get;
|
||||
@@ -32,7 +32,7 @@ namespace Barotrauma.Items.Components
|
||||
private Character currentUser;
|
||||
private Submarine currentSub;
|
||||
|
||||
public RemoteController(Item item, XElement element)
|
||||
public RemoteController(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
}
|
||||
@@ -81,7 +81,7 @@ namespace Barotrauma.Items.Components
|
||||
if (targetItem.Submarine != item.Submarine) { continue; }
|
||||
if (targetItem.Submarine.TeamID != user.TeamID) { continue; }
|
||||
}
|
||||
if (!targetItem.HasTag(Target) && targetItem.prefab.Identifier != Target) { continue; }
|
||||
if (!targetItem.HasTag(Target) && ((MapEntity)targetItem).Prefab.Identifier != Target) { continue; }
|
||||
|
||||
float distSqr = Vector2.DistanceSquared(item.WorldPosition, targetItem.WorldPosition);
|
||||
if (distSqr > Range * Range || distSqr > closestDist) { continue; }
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class Repairable : ItemComponent, IServerSerializable, IClientSerializable
|
||||
{
|
||||
private readonly string header;
|
||||
private readonly LocalizedString header;
|
||||
|
||||
private float deteriorationTimer;
|
||||
private float deteriorateAlwaysResetTimer;
|
||||
@@ -24,63 +24,63 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public float LastActiveTime;
|
||||
|
||||
[Serialize(0.0f, true, description: "How fast the condition of the item deteriorates per second."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, DecimalCount = 2)]
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes, description: "How fast the condition of the item deteriorates per second."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, DecimalCount = 2)]
|
||||
public float DeteriorationSpeed
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(0.0f, true, description: "Minimum initial delay before the item starts to deteriorate."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f, DecimalCount = 2)]
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes, description: "Minimum initial delay before the item starts to deteriorate."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f, DecimalCount = 2)]
|
||||
public float MinDeteriorationDelay
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(0.0f, true, description: "Maximum initial delay before the item starts to deteriorate."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f, DecimalCount = 2)]
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes, description: "Maximum initial delay before the item starts to deteriorate."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f, DecimalCount = 2)]
|
||||
public float MaxDeteriorationDelay
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(50.0f, true, description: "The item won't deteriorate spontaneously if the condition is below this value. For example, if set to 10, the condition will spontaneously drop to 10 and then stop dropping (unless the item is damaged further by external factors). Percentages of max condition."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f)]
|
||||
[Serialize(50.0f, IsPropertySaveable.Yes, description: "The item won't deteriorate spontaneously if the condition is below this value. For example, if set to 10, the condition will spontaneously drop to 10 and then stop dropping (unless the item is damaged further by external factors). Percentages of max condition."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f)]
|
||||
public float MinDeteriorationCondition
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(0f, true, description: "How low a traitor must get the item's condition for it to start breaking down.")]
|
||||
[Serialize(0f, IsPropertySaveable.Yes, description: "How low a traitor must get the item's condition for it to start breaking down.")]
|
||||
public float MinSabotageCondition
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(80.0f, true, description: "The condition of the item has to be below this for it to become repairable. Percentages of max condition."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f)]
|
||||
[Serialize(80.0f, IsPropertySaveable.Yes, description: "The condition of the item has to be below this for it to become repairable. Percentages of max condition."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f)]
|
||||
public float RepairThreshold
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(100.0f, true, description: "The amount of time it takes to fix the item with insufficient skill levels."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f)]
|
||||
[Serialize(100.0f, IsPropertySaveable.Yes, description: "The amount of time it takes to fix the item with insufficient skill levels."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f)]
|
||||
public float FixDurationLowSkill
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(10.0f, true, description: "The amount of time it takes to fix the item with sufficient skill levels."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f)]
|
||||
[Serialize(10.0f, IsPropertySaveable.Yes, description: "The amount of time it takes to fix the item with sufficient skill levels."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f)]
|
||||
public float FixDurationHighSkill
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "If set to true, the deterioration timer will always run regardless if the item is being used or not.")]
|
||||
[Serialize(false, IsPropertySaveable.No, description: "If set to true, the deterioration timer will always run regardless if the item is being used or not.")]
|
||||
public bool DeteriorateAlways
|
||||
{
|
||||
get;
|
||||
@@ -89,7 +89,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private float skillRequirementMultiplier;
|
||||
|
||||
[Serialize(1.0f, true)]
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes)]
|
||||
public float SkillRequirementMultiplier
|
||||
{
|
||||
get { return skillRequirementMultiplier; }
|
||||
@@ -137,7 +137,7 @@ namespace Barotrauma.Items.Components
|
||||
private set { currentFixerAction = value; }
|
||||
}
|
||||
|
||||
public Repairable(Item item, XElement element)
|
||||
public Repairable(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
@@ -145,9 +145,9 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
this.item = item;
|
||||
header =
|
||||
TextManager.Get(element.GetAttributeString("header", ""), returnNull: true) ??
|
||||
TextManager.Get(item.Prefab.ConfigElement.GetAttributeString("header", ""), returnNull: true) ??
|
||||
element.GetAttributeString("name", "");
|
||||
TextManager.Get(element.GetAttributeString("header", "")).Fallback(
|
||||
TextManager.Get(item.Prefab.ConfigElement.GetAttributeString("header", ""))).Fallback(
|
||||
element.GetAttributeString("name", ""));
|
||||
|
||||
//backwards compatibility
|
||||
var repairThresholdAttribute =
|
||||
@@ -169,7 +169,7 @@ namespace Barotrauma.Items.Components
|
||||
deteriorationTimer = Rand.Range(MinDeteriorationDelay, MaxDeteriorationDelay);
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(XElement element);
|
||||
partial void InitProjSpecific(ContentXElement element);
|
||||
|
||||
/// <summary>
|
||||
/// Check if the character manages to succesfully repair the item
|
||||
@@ -183,7 +183,7 @@ namespace Barotrauma.Items.Components
|
||||
if (bestRepairItem != null && bestRepairItem.Prefab.CannotRepairFail) { return true; }
|
||||
|
||||
// unpowered (electrical) items can be repaired without a risk of electrical shock
|
||||
if (requiredSkills.Any(s => s != null && s.Identifier.Equals("electrical", StringComparison.OrdinalIgnoreCase)))
|
||||
if (requiredSkills.Any(s => s != null && s.Identifier == "electrical"))
|
||||
{
|
||||
if (item.GetComponent<Reactor>() is Reactor reactor)
|
||||
{
|
||||
@@ -230,7 +230,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
ApplyStatusEffects(ActionType.OnFailure, 1.0f, CurrentFixer);
|
||||
#if SERVER
|
||||
GameMain.Server?.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnFailure, this, CurrentFixer.ID });
|
||||
GameMain.Server?.CreateEntityEvent(item, new Item.ApplyStatusEffectEventData(ActionType.OnFailure, this, CurrentFixer));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -244,6 +244,11 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
if (CurrentFixerAction == FixActions.Tinker && action != FixActions.Tinker)
|
||||
{
|
||||
CurrentFixer?.CheckTalents(AbilityEffectType.OnStopTinkering);
|
||||
}
|
||||
|
||||
Item bestRepairItem = GetBestRepairItem(character);
|
||||
#if SERVER
|
||||
if (CurrentFixer != character || currentFixerAction != action)
|
||||
@@ -251,17 +256,22 @@ namespace Barotrauma.Items.Components
|
||||
if (!CheckCharacterSuccess(character, bestRepairItem))
|
||||
{
|
||||
GameServer.Log($"{GameServer.CharacterLogName(character)} failed to {(action == FixActions.Sabotage ? "sabotage" : "repair")} {item.Name}", ServerLog.MessageType.ItemInteraction);
|
||||
GameMain.Server?.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnFailure, this, character.ID });
|
||||
GameMain.Server?.CreateEntityEvent(item, new Item.ApplyStatusEffectEventData(ActionType.OnFailure, this, character));
|
||||
if (bestRepairItem != null && bestRepairItem.GetComponent<Holdable>() is Holdable h)
|
||||
{
|
||||
GameMain.Server?.CreateEntityEvent(bestRepairItem, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnFailure, h, character.ID });
|
||||
GameMain.Server?.CreateEntityEvent(bestRepairItem, new Item.ApplyStatusEffectEventData(ActionType.OnFailure, h, character));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
GameServer.Log($"{GameServer.CharacterLogName(character)} started {(action == FixActions.Sabotage ? "sabotaging" : "repairing")} {item.Name}", ServerLog.MessageType.ItemInteraction);
|
||||
item.CreateServerEvent(this);
|
||||
if ((character != prevLoggedFixer || action != prevLoggedFixAction) && (character.TeamID == CharacterTeamType.Team1 || character.TeamID == CharacterTeamType.Team2))
|
||||
{
|
||||
GameServer.Log($"{GameServer.CharacterLogName(character)} started {(action == FixActions.Sabotage ? "sabotaging" : "repairing")} {item.Name}", ServerLog.MessageType.ItemInteraction);
|
||||
item.CreateServerEvent(this);
|
||||
prevLoggedFixer = character;
|
||||
prevLoggedFixAction = action;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (GameMain.Client == null && (CurrentFixer != character || currentFixerAction != action) && !CheckCharacterSuccess(character, bestRepairItem)) { return false; }
|
||||
@@ -442,7 +452,7 @@ namespace Barotrauma.Items.Components
|
||||
float fixDuration = MathHelper.Lerp(FixDurationLowSkill, FixDurationHighSkill, successFactor);
|
||||
fixDuration /= 1 + CurrentFixer.GetStatValue(StatTypes.RepairSpeed) + currentRepairItem?.Prefab.AddedRepairSpeedMultiplier ?? 0f;
|
||||
fixDuration /= 1 + item.GetQualityModifier(Quality.StatType.RepairSpeed);
|
||||
|
||||
|
||||
item.MaxRepairConditionMultiplier = GetMaxRepairConditionMultiplier(CurrentFixer);
|
||||
|
||||
if (currentFixerAction == FixActions.Repair)
|
||||
@@ -478,6 +488,10 @@ namespace Barotrauma.Items.Components
|
||||
deteriorationTimer = Rand.Range(MinDeteriorationDelay, MaxDeteriorationDelay);
|
||||
wasBroken = false;
|
||||
StopRepairing(CurrentFixer);
|
||||
#if SERVER
|
||||
prevLoggedFixer = null;
|
||||
prevLoggedFixAction = FixActions.None;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else if (currentFixerAction == FixActions.Sabotage)
|
||||
@@ -523,11 +537,11 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (character == null) { return 1.0f; }
|
||||
// kind of rough to keep this in update, but seems most robust
|
||||
if (requiredSkills.Any(s => s != null && s.Identifier.Equals("mechanical", StringComparison.OrdinalIgnoreCase)))
|
||||
if (requiredSkills.Any(s => s != null && s.Identifier == "mechanical"))
|
||||
{
|
||||
return 1 + character.GetStatValue(StatTypes.MaxRepairConditionMultiplierMechanical);
|
||||
}
|
||||
if (requiredSkills.Any(s => s != null && s.Identifier.Equals("electrical", StringComparison.OrdinalIgnoreCase)))
|
||||
if (requiredSkills.Any(s => s != null && s.Identifier == "electrical"))
|
||||
{
|
||||
return 1 + character.GetStatValue(StatTypes.MaxRepairConditionMultiplierElectrical);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
using FarseerPhysics;
|
||||
using FarseerPhysics.Dynamics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -12,62 +12,97 @@ namespace Barotrauma.Items.Components
|
||||
private ISpatialEntity source;
|
||||
private Item target;
|
||||
|
||||
private Vector2? launchDir;
|
||||
|
||||
private void SetSource(ISpatialEntity source)
|
||||
{
|
||||
this.source = source;
|
||||
if (source is Limb sourceLimb)
|
||||
{
|
||||
sourceLimb.AttachedRope = this;
|
||||
float offset = sourceLimb.Params.GetSpriteOrientation() - MathHelper.PiOver2;
|
||||
launchDir = VectorExtensions.Forward(sourceLimb.body.TransformedRotation - offset * sourceLimb.character.AnimController.Dir);
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetSource()
|
||||
{
|
||||
if (source is Limb sourceLimb && sourceLimb.AttachedRope == this)
|
||||
{
|
||||
sourceLimb.AttachedRope = null;
|
||||
}
|
||||
source = null;
|
||||
}
|
||||
|
||||
private float snapTimer;
|
||||
private const float SnapAnimDuration = 1.0f;
|
||||
|
||||
[Serialize(1.0f, IsPropertySaveable.No, description: "")]
|
||||
public float SnapAnimDuration
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
private float raycastTimer;
|
||||
private const float RayCastInterval = 0.2f;
|
||||
|
||||
[Serialize(0.0f, false, description: "How much force is applied to pull the projectile the rope is attached to.")]
|
||||
[Serialize(0.0f, IsPropertySaveable.No, description: "How much force is applied to pull the projectile the rope is attached to.")]
|
||||
public float ProjectilePullForce
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(0.0f, false, description: "How much force is applied to pull the target the rope is attached to.")]
|
||||
[Serialize(0.0f, IsPropertySaveable.No, description: "How much force is applied to pull the target the rope is attached to.")]
|
||||
public float TargetPullForce
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(0.0f, false, description: "How much force is applied to pull the source the rope is attached to.")]
|
||||
[Serialize(0.0f, IsPropertySaveable.No, description: "How much force is applied to pull the source the rope is attached to.")]
|
||||
public float SourcePullForce
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(1000.0f, false, description: "How far the source item can be from the projectile until the rope breaks.")]
|
||||
[Serialize(1000.0f, IsPropertySaveable.No, description: "How far the source item can be from the projectile until the rope breaks.")]
|
||||
public float MaxLength
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(true, false, description: "Should the rope snap when it collides with a structure/submarine (if not, it will just go through it).")]
|
||||
[Serialize(360.0f, IsPropertySaveable.No, description: "The maximum angle from the source to the target until the rope breaks.")]
|
||||
public float MaxAngle
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(true, IsPropertySaveable.No, description: "Should the rope snap when it collides with a structure/submarine (if not, it will just go through it).")]
|
||||
public bool SnapOnCollision
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(true, false, description: "Should the rope snap when the character drops the aim?")]
|
||||
[Serialize(true, IsPropertySaveable.No, description: "Should the rope snap when the character drops the aim?")]
|
||||
public bool SnapWhenNotAimed
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(30.0f, false, description: "How much mass is required for the target to pull the source towards it. Static and kinematic targets are always treated heavy enough.")]
|
||||
[Serialize(30.0f, IsPropertySaveable.No, description: "How much mass is required for the target to pull the source towards it. Static and kinematic targets are always treated heavy enough.")]
|
||||
public float TargetMinMass
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, false)]
|
||||
[Serialize(false, IsPropertySaveable.No)]
|
||||
public bool LerpForces
|
||||
{
|
||||
get;
|
||||
@@ -102,12 +137,12 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public Rope(Item item, XElement element) : base(item, element)
|
||||
public Rope(Item item, ContentXElement element) : base(item, element)
|
||||
{
|
||||
InitProjSpecific(element);
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(XElement element);
|
||||
partial void InitProjSpecific(ContentXElement element);
|
||||
|
||||
public void Snap() => Snapped = true;
|
||||
|
||||
@@ -115,8 +150,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(source != null);
|
||||
System.Diagnostics.Debug.Assert(target != null);
|
||||
this.source = source;
|
||||
this.target = target;
|
||||
SetSource(source);
|
||||
Snapped = false;
|
||||
ApplyStatusEffects(ActionType.OnUse, 1.0f, worldPosition: item.WorldPosition);
|
||||
IsActive = true;
|
||||
@@ -127,7 +162,7 @@ namespace Barotrauma.Items.Components
|
||||
if (source == null || target == null || target.Removed ||
|
||||
(source is Entity sourceEntity && sourceEntity.Removed))
|
||||
{
|
||||
source = null;
|
||||
ResetSource();
|
||||
target = null;
|
||||
IsActive = false;
|
||||
return;
|
||||
@@ -144,12 +179,27 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
Vector2 diff = target.WorldPosition - source.WorldPosition;
|
||||
if (diff.LengthSquared() > MaxLength * MaxLength)
|
||||
float lengthSqr = diff.LengthSquared();
|
||||
if (lengthSqr > MaxLength * MaxLength)
|
||||
{
|
||||
Snap();
|
||||
return;
|
||||
}
|
||||
|
||||
if (MaxAngle < 180 && lengthSqr > 2500)
|
||||
{
|
||||
if (launchDir == null)
|
||||
{
|
||||
launchDir = diff;
|
||||
}
|
||||
float angle = MathHelper.ToDegrees(VectorExtensions.Angle(launchDir.Value, diff));
|
||||
if (angle > MaxAngle)
|
||||
{
|
||||
Snap();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
item.ResetCachedVisibleSize();
|
||||
#endif
|
||||
|
||||
@@ -5,9 +5,9 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class Scanner : ItemComponent
|
||||
{
|
||||
[Serialize(1.0f, false, description: "How long it takes for the scan to be completed.")]
|
||||
[Serialize(1.0f, IsPropertySaveable.No, description: "How long it takes for the scan to be completed.")]
|
||||
public float ScanDuration { get; set; }
|
||||
[Serialize(0.0f, false, description: "How far along the scan is. When the timer goes above ScanDuration, the scan is completed.")]
|
||||
[Serialize(0.0f, IsPropertySaveable.No, description: "How far along the scan is. When the timer goes above ScanDuration, the scan is completed.")]
|
||||
public float ScanTimer
|
||||
{
|
||||
get
|
||||
@@ -33,9 +33,9 @@ namespace Barotrauma.Items.Components
|
||||
#endif
|
||||
}
|
||||
}
|
||||
[Serialize(1.0f, false, description: "How far the scanner can be from the target for the scan to be successful.")]
|
||||
[Serialize(1.0f, IsPropertySaveable.No, description: "How far the scanner can be from the target for the scan to be successful.")]
|
||||
public float ScanRadius { get; set; }
|
||||
[Serialize(true, false, description: "Should the progress bar always be displayed when the item has been attached.")]
|
||||
[Serialize(true, IsPropertySaveable.No, description: "Should the progress bar always be displayed when the item has been attached.")]
|
||||
public bool AlwaysDisplayProgressBar { get; set; }
|
||||
|
||||
private Holdable Holdable { get; set; }
|
||||
@@ -49,7 +49,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public Action<Scanner> OnScanStarted, OnScanCompleted;
|
||||
|
||||
public Scanner(Item item, XElement element) : base(item, element)
|
||||
public Scanner(Item item, ContentXElement element) : base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
class AdderComponent : ArithmeticComponent
|
||||
{
|
||||
public AdderComponent(Item item, XElement element)
|
||||
public AdderComponent(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
protected readonly Character[] signalSender = new Character[2];
|
||||
|
||||
[InGameEditable(DecimalCount = 2), Serialize(0.0f, true, description: "The item sends the output if both inputs have received a non-zero signal within the timeframe. If set to 0, the inputs must receive a signal at the same time.", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable(DecimalCount = 2), Serialize(0.0f, IsPropertySaveable.Yes, description: "The item sends the output if both inputs have received a non-zero signal within the timeframe. If set to 0, the inputs must receive a signal at the same time.", alwaysUseInstanceValues: true)]
|
||||
public float TimeFrame
|
||||
{
|
||||
get { return timeFrame; }
|
||||
@@ -30,7 +30,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
private int maxOutputLength;
|
||||
[Editable, Serialize(200, false, description: "The maximum length of the output strings. Warning: Large values can lead to large memory usage or networking issues.")]
|
||||
[Editable, Serialize(200, IsPropertySaveable.No, description: "The maximum length of the output strings. Warning: Large values can lead to large memory usage or networking issues.")]
|
||||
public int MaxOutputLength
|
||||
{
|
||||
get { return maxOutputLength; }
|
||||
@@ -40,7 +40,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[InGameEditable, Serialize("1", true, description: "The signal sent when the condition is met.", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize("1", IsPropertySaveable.Yes, description: "The signal sent when the condition is met.", alwaysUseInstanceValues: true)]
|
||||
public string Output
|
||||
{
|
||||
get { return output; }
|
||||
@@ -55,7 +55,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[InGameEditable, Serialize("", true, description: "The signal sent when the condition is met (if empty, no signal is sent).", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize("", IsPropertySaveable.Yes, description: "The signal sent when the condition is met (if empty, no signal is sent).", alwaysUseInstanceValues: true)]
|
||||
public string FalseOutput
|
||||
{
|
||||
get { return falseOutput; }
|
||||
@@ -70,7 +70,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public AndComponent(Item item, XElement element)
|
||||
public AndComponent(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
timeSinceReceived = new float[] { Math.Max(timeFrame * 2.0f, 0.1f), Math.Max(timeFrame * 2.0f, 0.1f) };
|
||||
|
||||
+10
-6
@@ -15,7 +15,9 @@ namespace Barotrauma.Items.Components
|
||||
//the output is sent if both inputs have received a signal within the timeframe
|
||||
protected float timeFrame;
|
||||
|
||||
[Serialize(999999.0f, true, description: "The output of the item is restricted below this value.", alwaysUseInstanceValues: true),
|
||||
protected readonly Character[] signalSender = new Character[2];
|
||||
|
||||
[Serialize(999999.0f, IsPropertySaveable.Yes, description: "The output of the item is restricted below this value.", alwaysUseInstanceValues: true),
|
||||
InGameEditable(MinValueFloat = -999999.0f, MaxValueFloat = 999999.0f)]
|
||||
public float ClampMax
|
||||
{
|
||||
@@ -23,7 +25,7 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(-999999.0f, true, description: "The output of the item is restricted above this value.", alwaysUseInstanceValues: true),
|
||||
[Serialize(-999999.0f, IsPropertySaveable.Yes, description: "The output of the item is restricted above this value.", alwaysUseInstanceValues: true),
|
||||
InGameEditable(MinValueFloat = -999999.0f, MaxValueFloat = 999999.0f)]
|
||||
public float ClampMin
|
||||
{
|
||||
@@ -32,8 +34,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
[InGameEditable(DecimalCount = 2),
|
||||
Serialize(0.0f, true, description: "The item must have received signals to both inputs within this timeframe to output the result." +
|
||||
" If set to 0, the inputs must be received at the same time.", alwaysUseInstanceValues: true)]
|
||||
Serialize(0.0f, IsPropertySaveable.Yes, description: "The item must have received signals to both inputs within this timeframe to output the result." +
|
||||
" If set to 0, the inputs must be received at the same time.", alwaysUseInstanceValues: true, translationTextTag: "sp.")]
|
||||
public float TimeFrame
|
||||
{
|
||||
get { return timeFrame; }
|
||||
@@ -47,7 +49,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public ArithmeticComponent(Item item, XElement element)
|
||||
public ArithmeticComponent(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
timeSinceReceived = new float[] { Math.Max(timeFrame * 2.0f, 0.1f), Math.Max(timeFrame * 2.0f, 0.1f) };
|
||||
@@ -71,7 +73,7 @@ namespace Barotrauma.Items.Components
|
||||
float output = Calculate(receivedSignal[0], receivedSignal[1]);
|
||||
if (MathUtils.IsValid(output))
|
||||
{
|
||||
item.SendSignal(MathHelper.Clamp(output, ClampMin, ClampMax).ToString("G", CultureInfo.InvariantCulture), "signal_out");
|
||||
item.SendSignal(new Signal(MathHelper.Clamp(output, ClampMin, ClampMax).ToString("G", CultureInfo.InvariantCulture), sender: signalSender[0] ?? signalSender[1]), "signal_out");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,11 +85,13 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
case "signal_in1":
|
||||
float.TryParse(signal.value, NumberStyles.Float, CultureInfo.InvariantCulture, out receivedSignal[0]);
|
||||
signalSender[0] = signal.sender;
|
||||
timeSinceReceived[0] = 0.0f;
|
||||
IsActive = true;
|
||||
break;
|
||||
case "signal_in2":
|
||||
float.TryParse(signal.value, NumberStyles.Float, CultureInfo.InvariantCulture, out receivedSignal[1]);
|
||||
signalSender[1] = signal.sender;
|
||||
timeSinceReceived[1] = 0.0f;
|
||||
IsActive = true;
|
||||
break;
|
||||
|
||||
@@ -9,9 +9,9 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class ButtonTerminal : ItemComponent
|
||||
{
|
||||
[Editable, Serialize(new string[0], true, description: "Signals sent when the corresponding buttons are pressed.", alwaysUseInstanceValues: true)]
|
||||
[Editable, Serialize(new string[0], IsPropertySaveable.Yes, description: "Signals sent when the corresponding buttons are pressed.", alwaysUseInstanceValues: true)]
|
||||
public string[] Signals { get; set; }
|
||||
[Editable, Serialize("", true, description: "Identifiers or tags of items that, when contained, allow the terminal buttons to be used. Multiple ones should be separated by commas.", alwaysUseInstanceValues: true)]
|
||||
[Editable, Serialize("", IsPropertySaveable.Yes, description: "Identifiers or tags of items that, when contained, allow the terminal buttons to be used. Multiple ones should be separated by commas.", alwaysUseInstanceValues: true)]
|
||||
public string ActivatingItems { get; set; }
|
||||
|
||||
private int RequiredSignalCount { get; set; }
|
||||
@@ -21,7 +21,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private bool AllowUsingButtons => ActivatingItemPrefabs.None() || (Container != null && Container.Inventory.AllItems.Any(i => i != null && ActivatingItemPrefabs.Any(p => p == i.Prefab)));
|
||||
|
||||
public ButtonTerminal(Item item, XElement element) : base(item, element)
|
||||
public ButtonTerminal(Item item, ContentXElement element) : base(item, element)
|
||||
{
|
||||
RequiredSignalCount = element.GetChildElements("TerminalButton").Count(c => c.GetAttribute("style") != null);
|
||||
if (RequiredSignalCount < 1)
|
||||
@@ -31,7 +31,7 @@ namespace Barotrauma.Items.Components
|
||||
InitProjSpecific(element);
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(XElement element);
|
||||
partial void InitProjSpecific(ContentXElement element);
|
||||
|
||||
public override void OnItemLoaded()
|
||||
{
|
||||
@@ -77,7 +77,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
ItemPrefab.Prefabs.Where(p => p.Tags.Any(t => t.Equals(activatingItem, StringComparison.OrdinalIgnoreCase)))
|
||||
ItemPrefab.Prefabs.Where(p => p.Tags.Any(t => t == activatingItem))
|
||||
.ForEach(p => ActivatingItemPrefabs.Add(p));
|
||||
}
|
||||
}
|
||||
@@ -109,10 +109,23 @@ namespace Barotrauma.Items.Components
|
||||
return true;
|
||||
}
|
||||
|
||||
private void Write(IWriteMessage msg, object[] extraData)
|
||||
private readonly struct EventData : IEventData
|
||||
{
|
||||
if (extraData == null || extraData.Length < 3) { return; }
|
||||
msg.WriteRangedInteger((int)extraData[2], 0, Signals.Length - 1);
|
||||
public readonly int SignalIndex;
|
||||
|
||||
public EventData(int signalIndex)
|
||||
{
|
||||
SignalIndex = signalIndex;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool ValidateEventData(NetEntityEvent.IData data)
|
||||
=> TryExtractEventData<EventData>(data, out _);
|
||||
|
||||
private void Write(IWriteMessage msg, NetEntityEvent.IData extraData)
|
||||
{
|
||||
var eventData = ExtractEventData<EventData>(extraData);
|
||||
msg.WriteRangedInteger(eventData.SignalIndex, 0, Signals.Length - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,10 +11,10 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private string output = "0,0,0,0";
|
||||
|
||||
[InGameEditable, Serialize(false, true, description: "When enabled makes the component translate the signal from HSV into RGB where red is the hue between 0 and 360, green is the saturation between 0 and 1 and blue is the value between 0 and 1.", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize(false, IsPropertySaveable.Yes, description: "When enabled makes the component translate the signal from HSV into RGB where red is the hue between 0 and 360, green is the saturation between 0 and 1 and blue is the value between 0 and 1.", alwaysUseInstanceValues: true)]
|
||||
public bool UseHSV { get; set; }
|
||||
|
||||
public ColorComponent(Item item, XElement element)
|
||||
public ColorComponent(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
receivedSignal = new float[4];
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
private int maxOutputLength;
|
||||
|
||||
[Editable, Serialize(256, false, description: "The maximum length of the output string. Warning: Large values can lead to large memory usage or networking load.")]
|
||||
[Editable, Serialize(256, IsPropertySaveable.No, description: "The maximum length of the output string. Warning: Large values can lead to large memory usage or networking load.")]
|
||||
public int MaxOutputLength
|
||||
{
|
||||
get { return maxOutputLength; }
|
||||
@@ -17,14 +17,14 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize("", false)]
|
||||
[InGameEditable, Serialize("", IsPropertySaveable.No)]
|
||||
public string Separator
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public ConcatComponent(Item item, XElement element)
|
||||
public ConcatComponent(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Barotrauma.Items.Components
|
||||
public readonly int MaxWires = 5;
|
||||
|
||||
public readonly string Name;
|
||||
public readonly string DisplayName;
|
||||
public readonly LocalizedString DisplayName;
|
||||
|
||||
private readonly Wire[] wires;
|
||||
public IEnumerable<Wire> Wires
|
||||
@@ -33,6 +33,12 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public readonly ushort[] wireId;
|
||||
|
||||
//The grid the connection is a part of
|
||||
public GridInfo Grid;
|
||||
|
||||
//Priority in which power output will be handled - load is unaffected
|
||||
public PowerPriority Priority = PowerPriority.Default;
|
||||
|
||||
public bool IsPower
|
||||
{
|
||||
get;
|
||||
@@ -40,7 +46,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
private bool recipientsDirty = true;
|
||||
private List<Connection> recipients = new List<Connection>();
|
||||
private readonly List<Connection> recipients = new List<Connection>();
|
||||
public List<Connection> Recipients
|
||||
{
|
||||
get
|
||||
@@ -66,17 +72,17 @@ namespace Barotrauma.Items.Components
|
||||
return "Connection (" + item.Name + ", " + Name + ")";
|
||||
}
|
||||
|
||||
public Connection(XElement element, ConnectionPanel connectionPanel, IdRemap idRemap)
|
||||
public Connection(ContentXElement element, ConnectionPanel connectionPanel, IdRemap idRemap)
|
||||
{
|
||||
|
||||
#if CLIENT
|
||||
if (connector == null)
|
||||
{
|
||||
connector = GUI.Style.GetComponentStyle("ConnectionPanelConnector").GetDefaultSprite();
|
||||
wireVertical = GUI.Style.GetComponentStyle("ConnectionPanelWire").GetDefaultSprite();
|
||||
connectionSprite = GUI.Style.GetComponentStyle("ConnectionPanelConnection").GetDefaultSprite();
|
||||
connectionSpriteHighlight = GUI.Style.GetComponentStyle("ConnectionPanelConnection").GetSprite(GUIComponent.ComponentState.Hover);
|
||||
screwSprites = GUI.Style.GetComponentStyle("ConnectionPanelScrew").Sprites[GUIComponent.ComponentState.None].Select(s => s.Sprite).ToList();
|
||||
connector = GUIStyle.GetComponentStyle("ConnectionPanelConnector").GetDefaultSprite();
|
||||
wireVertical = GUIStyle.GetComponentStyle("ConnectionPanelWire").GetDefaultSprite();
|
||||
connectionSprite = GUIStyle.GetComponentStyle("ConnectionPanelConnection").GetDefaultSprite();
|
||||
connectionSpriteHighlight = GUIStyle.GetComponentStyle("ConnectionPanelConnection").GetSprite(GUIComponent.ComponentState.Hover);
|
||||
screwSprites = GUIStyle.GetComponentStyle("ConnectionPanelScrew").Sprites[GUIComponent.ComponentState.None].Select(s => s.Sprite).ToList();
|
||||
}
|
||||
#endif
|
||||
ConnectionPanel = connectionPanel;
|
||||
@@ -93,9 +99,9 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
string displayNameTag = "", fallbackTag = "";
|
||||
//if displayname is not present, attempt to find it from the prefab
|
||||
if (element.Attribute("displayname") == null)
|
||||
if (element.GetAttribute("displayname") == null)
|
||||
{
|
||||
foreach (XElement subElement in item.Prefab.ConfigElement.Elements())
|
||||
foreach (var subElement in item.Prefab.ConfigElement.Elements())
|
||||
{
|
||||
if (!subElement.Name.ToString().Equals("connectionpanel", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
|
||||
@@ -132,7 +138,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(DisplayName))
|
||||
if (DisplayName.IsNullOrEmpty())
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError("Missing display name in connection " + item.Name + ": " + Name);
|
||||
@@ -145,7 +151,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
wireId = new ushort[MaxWires];
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
@@ -236,6 +242,34 @@ namespace Barotrauma.Items.Components
|
||||
var otherConnection = previousWire.OtherConnection(this);
|
||||
if (otherConnection != null)
|
||||
{
|
||||
//Change the connection grids or flag them for updating
|
||||
if (IsPower && otherConnection.IsPower && Grid != null)
|
||||
{
|
||||
//Check if both connections belong to a larger grid
|
||||
if (otherConnection.recipients.Count > 1 && recipients.Count > 1)
|
||||
{
|
||||
Powered.ChangedConnections.Add(otherConnection);
|
||||
Powered.ChangedConnections.Add(this);
|
||||
}
|
||||
else if (recipients.Count > 1)
|
||||
{
|
||||
//This wire was the only one at the other grid
|
||||
otherConnection.Grid?.RemoveConnection(otherConnection);
|
||||
otherConnection.Grid = null;
|
||||
}
|
||||
else if (otherConnection.recipients.Count > 1)
|
||||
{
|
||||
Grid?.RemoveConnection(this);
|
||||
Grid = null;
|
||||
}
|
||||
else if (Grid.Connections.Count == 2)
|
||||
{
|
||||
//Delete the grid as these were the only 2 devices
|
||||
Powered.Grids.Remove(Grid.ID);
|
||||
Grid = null;
|
||||
otherConnection.Grid = null;
|
||||
}
|
||||
}
|
||||
otherConnection.recipientsDirty = true;
|
||||
}
|
||||
}
|
||||
@@ -244,10 +278,32 @@ namespace Barotrauma.Items.Components
|
||||
recipientsDirty = true;
|
||||
if (wire != null)
|
||||
{
|
||||
|
||||
ConnectionPanel.DisconnectedWires.Remove(wire);
|
||||
var otherConnection = wire.OtherConnection(this);
|
||||
if (otherConnection != null)
|
||||
{
|
||||
//Set the other connection grid if a grid exists already
|
||||
if (Powered.ValidPowerConnection(this, otherConnection))
|
||||
{
|
||||
if (Grid == null && otherConnection.Grid != null)
|
||||
{
|
||||
otherConnection.Grid.AddConnection(this);
|
||||
Grid = otherConnection.Grid;
|
||||
}
|
||||
else if (Grid != null && otherConnection.Grid == null)
|
||||
{
|
||||
Grid.AddConnection(otherConnection);
|
||||
otherConnection.Grid = Grid;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Flag change so that proper grids can be formed
|
||||
Powered.ChangedConnections.Add(this);
|
||||
Powered.ChangedConnections.Add(otherConnection);
|
||||
}
|
||||
}
|
||||
|
||||
otherConnection.recipientsDirty = true;
|
||||
}
|
||||
}
|
||||
@@ -286,20 +342,17 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void SendPowerProbeSignal(Item source, float power)
|
||||
{
|
||||
for (int i = 0; i < MaxWires; i++)
|
||||
{
|
||||
if (wires[i] == null) { continue; }
|
||||
|
||||
Connection recipient = wires[i].OtherConnection(this);
|
||||
if (recipient == null || !recipient.IsPower) { continue; }
|
||||
|
||||
recipient.item.GetComponent<Powered>()?.ReceivePowerProbeSignal(recipient, source, power);
|
||||
}
|
||||
}
|
||||
public void ClearConnections()
|
||||
{
|
||||
if (IsPower && Grid != null)
|
||||
{
|
||||
Powered.ChangedConnections.Add(this);
|
||||
foreach (Connection c in recipients)
|
||||
{
|
||||
Powered.ChangedConnections.Add(c);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < MaxWires; i++)
|
||||
{
|
||||
if (wires[i] == null) continue;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Barotrauma.Networking;
|
||||
using System;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -39,7 +40,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, true, description: "Locked connection panels cannot be rewired in-game.", alwaysUseInstanceValues: true)]
|
||||
[Editable, Serialize(false, IsPropertySaveable.Yes, description: "Locked connection panels cannot be rewired in-game.", alwaysUseInstanceValues: true)]
|
||||
public bool Locked
|
||||
{
|
||||
get;
|
||||
@@ -63,12 +64,12 @@ namespace Barotrauma.Items.Components
|
||||
get { return user; }
|
||||
}
|
||||
|
||||
public ConnectionPanel(Item item, XElement element)
|
||||
public ConnectionPanel(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
Connections = new List<Connection>();
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString())
|
||||
{
|
||||
@@ -264,13 +265,13 @@ namespace Barotrauma.Items.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void Load(XElement element, bool usePrefabValues, IdRemap idRemap)
|
||||
public override void Load(ContentXElement element, bool usePrefabValues, IdRemap idRemap)
|
||||
{
|
||||
base.Load(element, usePrefabValues, idRemap);
|
||||
|
||||
List<Connection> loadedConnections = new List<Connection>();
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString())
|
||||
{
|
||||
@@ -306,7 +307,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
disconnectedWireIds = element.GetAttributeUshortArray("disconnectedwires", new ushort[0]).ToList();
|
||||
disconnectedWireIds = element.GetAttributeUshortArray("disconnectedwires", Array.Empty<ushort>()).ToList();
|
||||
for (int i = 0; i < disconnectedWireIds.Count; i++)
|
||||
{
|
||||
disconnectedWireIds[i] = idRemap.GetOffsetId(disconnectedWireIds[i]);
|
||||
@@ -399,7 +400,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
|
||||
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
|
||||
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
#if CLIENT
|
||||
TriggerRewiringSound();
|
||||
|
||||
+31
-20
@@ -1,4 +1,5 @@
|
||||
using Barotrauma.Networking;
|
||||
using System;
|
||||
using Barotrauma.Networking;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
@@ -7,20 +8,30 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class CustomInterface : ItemComponent, IClientSerializable, IServerSerializable
|
||||
{
|
||||
public class CustomInterfaceElement : ISerializableEntity
|
||||
private readonly struct EventData : IEventData
|
||||
{
|
||||
public readonly CustomInterfaceElement BtnElement;
|
||||
|
||||
public EventData(CustomInterfaceElement btnElement)
|
||||
{
|
||||
BtnElement = btnElement;
|
||||
}
|
||||
}
|
||||
|
||||
class CustomInterfaceElement : ISerializableEntity
|
||||
{
|
||||
public bool ContinuousSignal;
|
||||
public bool State;
|
||||
public string ConnectionName;
|
||||
public Connection Connection;
|
||||
|
||||
[Serialize("", false, translationTextTag: "Label.", description: "The text displayed on this button/tickbox."), Editable]
|
||||
[Serialize("", IsPropertySaveable.No, translationTextTag: "Label.", description: "The text displayed on this button/tickbox."), Editable]
|
||||
public string Label { get; set; }
|
||||
|
||||
[Serialize("1", false, description: "The signal sent out when this button is pressed or this tickbox checked."), Editable]
|
||||
[Serialize("1", IsPropertySaveable.No, description: "The signal sent out when this button is pressed or this tickbox checked."), Editable]
|
||||
public string Signal { get; set; }
|
||||
|
||||
public string PropertyName { get; }
|
||||
public Identifier PropertyName { get; }
|
||||
public bool TargetOnlyParentProperty { get; }
|
||||
|
||||
public int NumberInputMin { get; }
|
||||
@@ -35,7 +46,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public string Name => "CustomInterfaceElement";
|
||||
|
||||
public Dictionary<string, SerializableProperty> SerializableProperties { get; set; }
|
||||
public Dictionary<Identifier, SerializableProperty> SerializableProperties { get; set; }
|
||||
|
||||
public List<StatusEffect> StatusEffects = new List<StatusEffect>();
|
||||
|
||||
@@ -43,19 +54,19 @@ namespace Barotrauma.Items.Components
|
||||
/// Pass the parent component to the constructor to access the serializable properties
|
||||
/// for elements which change property values.
|
||||
/// </summary>
|
||||
public CustomInterfaceElement(XElement element, CustomInterface parent)
|
||||
public CustomInterfaceElement(Item item, ContentXElement element, CustomInterface parent)
|
||||
{
|
||||
Label = element.GetAttributeString("text", "");
|
||||
ConnectionName = element.GetAttributeString("connection", "");
|
||||
PropertyName = element.GetAttributeString("propertyname", "").ToLowerInvariant();
|
||||
PropertyName = element.GetAttributeIdentifier("propertyname", "");
|
||||
TargetOnlyParentProperty = element.GetAttributeBool("targetonlyparentproperty", false);
|
||||
NumberInputMin = element.GetAttributeInt("min", DefaultNumberInputMin);
|
||||
NumberInputMax = element.GetAttributeInt("max", DefaultNumberInputMax);
|
||||
MaxTextLength = element.GetAttributeInt("maxtextlength", int.MaxValue);
|
||||
HasPropertyName = !string.IsNullOrEmpty(PropertyName);
|
||||
HasPropertyName = !PropertyName.IsEmpty;
|
||||
IsIntegerInput = HasPropertyName && element.Name.ToString().ToLowerInvariant() == "integerinput";
|
||||
|
||||
if (element.Attribute("signal") is XAttribute attribute)
|
||||
if (element.GetAttribute("signal") is XAttribute attribute)
|
||||
{
|
||||
Signal = attribute.Value;
|
||||
ShouldSetProperty = HasPropertyName;
|
||||
@@ -84,7 +95,7 @@ namespace Barotrauma.Items.Components
|
||||
Signal = "1";
|
||||
}
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
if (subElement.Name.ToString().Equals("statuseffect", System.StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
@@ -95,7 +106,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
private string[] labels;
|
||||
[Serialize("", true, description: "The texts displayed on the buttons/tickboxes, separated by commas.", alwaysUseInstanceValues: true)]
|
||||
[Serialize("", IsPropertySaveable.Yes, description: "The texts displayed on the buttons/tickboxes, separated by commas.", alwaysUseInstanceValues: true)]
|
||||
public string Labels
|
||||
{
|
||||
get { return string.Join(",", labels); }
|
||||
@@ -104,14 +115,14 @@ namespace Barotrauma.Items.Components
|
||||
if (value == null) { return; }
|
||||
if (customInterfaceElementList.Count > 0)
|
||||
{
|
||||
string[] splitValues = value == "" ? new string[0] : value.Split(',');
|
||||
string[] splitValues = value == "" ? Array.Empty<string>() : value.Split(',');
|
||||
UpdateLabels(splitValues);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string[] signals;
|
||||
[Serialize("", true, description: "The signals sent when the buttons are pressed or the tickboxes checked, separated by commas.", alwaysUseInstanceValues: true)]
|
||||
[Serialize("", IsPropertySaveable.Yes, description: "The signals sent when the buttons are pressed or the tickboxes checked, separated by commas.", alwaysUseInstanceValues: true)]
|
||||
public string Signals
|
||||
{
|
||||
//use semicolon as a separator because comma may be needed in the signals (for color or vector values for example)
|
||||
@@ -122,7 +133,7 @@ namespace Barotrauma.Items.Components
|
||||
if (value == null) { return; }
|
||||
if (customInterfaceElementList.Count > 0)
|
||||
{
|
||||
string[] splitValues = value == "" ? new string[0] : value.Split(';');
|
||||
string[] splitValues = value == "" ? Array.Empty<string>() : value.Split(';');
|
||||
UpdateSignals(splitValues);
|
||||
}
|
||||
}
|
||||
@@ -132,17 +143,17 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public readonly List<CustomInterfaceElement> customInterfaceElementList = new List<CustomInterfaceElement>();
|
||||
|
||||
public CustomInterface(Item item, XElement element)
|
||||
public CustomInterface(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
foreach (XElement subElement in element.Elements())
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "button":
|
||||
case "textbox":
|
||||
case "integerinput":
|
||||
var button = new CustomInterfaceElement(subElement, this)
|
||||
var button = new CustomInterfaceElement(item, subElement, this)
|
||||
{
|
||||
ContinuousSignal = false
|
||||
};
|
||||
@@ -153,7 +164,7 @@ namespace Barotrauma.Items.Components
|
||||
customInterfaceElementList.Add(button);
|
||||
break;
|
||||
case "tickbox":
|
||||
var tickBox = new CustomInterfaceElement(subElement, this)
|
||||
var tickBox = new CustomInterfaceElement(item, subElement, this)
|
||||
{
|
||||
ContinuousSignal = true
|
||||
};
|
||||
@@ -179,7 +190,7 @@ namespace Barotrauma.Items.Components
|
||||
labels[i] = i < newLabels.Length ? newLabels[i] : customInterfaceElementList[i].Label;
|
||||
if (Screen.Selected != GameMain.SubEditorScreen)
|
||||
{
|
||||
customInterfaceElementList[i].Label = TextManager.Get(labels[i], returnNull: true) ?? labels[i];
|
||||
customInterfaceElementList[i].Label = TextManager.Get(labels[i]).Fallback(labels[i]).Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace Barotrauma.Items.Components
|
||||
private DelayedSignal prevQueuedSignal;
|
||||
|
||||
private float delay;
|
||||
[InGameEditable(MinValueFloat = 0.0f, MaxValueFloat = 60.0f, DecimalCount = 2), Serialize(1.0f, true, description: "How long the item delays the signals (in seconds).", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable(MinValueFloat = 0.0f, MaxValueFloat = 60.0f, DecimalCount = 2), Serialize(1.0f, IsPropertySaveable.Yes, description: "How long the item delays the signals (in seconds).", alwaysUseInstanceValues: true)]
|
||||
public float Delay
|
||||
{
|
||||
get { return delay; }
|
||||
@@ -44,21 +44,21 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[InGameEditable, Serialize(false, true, description: "Should the component discard previously received signals when a new one is received.", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize(false, IsPropertySaveable.Yes, description: "Should the component discard previously received signals when a new one is received.", alwaysUseInstanceValues: true)]
|
||||
public bool ResetWhenSignalReceived
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[InGameEditable, Serialize(false, true, description: "Should the component discard previously received signals when the incoming signal changes.", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize(false, IsPropertySaveable.Yes, description: "Should the component discard previously received signals when the incoming signal changes.", alwaysUseInstanceValues: true)]
|
||||
public bool ResetWhenDifferentSignalReceived
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public DelayComponent(Item item, XElement element)
|
||||
public DelayComponent(Item item, ContentXElement element)
|
||||
: base (item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
class DivideComponent : ArithmeticComponent
|
||||
{
|
||||
public DivideComponent(Item item, XElement element)
|
||||
public DivideComponent(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Barotrauma.Items.Components
|
||||
protected float timeFrame;
|
||||
|
||||
private int maxOutputLength;
|
||||
[Editable, Serialize(200, false, description: "The maximum length of the output strings. Warning: Large values can lead to large memory usage or networking issues.")]
|
||||
[Editable, Serialize(200, IsPropertySaveable.No, description: "The maximum length of the output strings. Warning: Large values can lead to large memory usage or networking issues.")]
|
||||
public int MaxOutputLength
|
||||
{
|
||||
get { return maxOutputLength; }
|
||||
@@ -28,7 +28,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[InGameEditable, Serialize("1", true, description: "The signal sent when the condition is met.", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize("1", IsPropertySaveable.Yes, description: "The signal sent when the condition is met.", alwaysUseInstanceValues: true)]
|
||||
public string Output
|
||||
{
|
||||
get { return output; }
|
||||
@@ -43,7 +43,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[InGameEditable, Serialize("", true, description: "The signal sent when the condition is met (if empty, no signal is sent).", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize("", IsPropertySaveable.Yes, description: "The signal sent when the condition is met (if empty, no signal is sent).", alwaysUseInstanceValues: true)]
|
||||
public string FalseOutput
|
||||
{
|
||||
get { return falseOutput; }
|
||||
@@ -58,7 +58,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[InGameEditable(DecimalCount = 2), Serialize(0.0f, true, description: "The maximum amount of time between the received signals. If set to 0, the signals must be received at the same time.", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable(DecimalCount = 2), Serialize(0.0f, IsPropertySaveable.Yes, description: "The maximum amount of time between the received signals. If set to 0, the signals must be received at the same time.", alwaysUseInstanceValues: true)]
|
||||
public float TimeFrame
|
||||
{
|
||||
get { return timeFrame; }
|
||||
@@ -72,7 +72,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public EqualsComponent(Item item, XElement element)
|
||||
public EqualsComponent(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
timeSinceReceived = new float[] { Math.Max(timeFrame * 2.0f, 0.1f), Math.Max(timeFrame * 2.0f, 0.1f) };
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@ namespace Barotrauma.Items.Components
|
||||
class ExponentiationComponent : ItemComponent
|
||||
{
|
||||
private float exponent;
|
||||
[InGameEditable, Serialize(1.0f, false, description: "The exponent of the operation.", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize(1.0f, IsPropertySaveable.No, description: "The exponent of the operation.", alwaysUseInstanceValues: true)]
|
||||
public float Exponent
|
||||
{
|
||||
get
|
||||
@@ -19,7 +19,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public ExponentiationComponent(Item item, XElement element)
|
||||
public ExponentiationComponent(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
|
||||
+2
-2
@@ -16,13 +16,13 @@ namespace Barotrauma.Items.Components
|
||||
SquareRoot
|
||||
}
|
||||
|
||||
[Serialize(FunctionType.Round, false, description: "Which kind of function to run the input through.", alwaysUseInstanceValues: true)]
|
||||
[Serialize(FunctionType.Round, IsPropertySaveable.No, description: "Which kind of function to run the input through.", alwaysUseInstanceValues: true)]
|
||||
public FunctionType Function
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public FunctionComponent(Item item, XElement element)
|
||||
public FunctionComponent(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
private float val1, val2;
|
||||
|
||||
public GreaterComponent(Item item, XElement element)
|
||||
public GreaterComponent(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
|
||||
+14
-16
@@ -30,7 +30,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private Turret turret;
|
||||
|
||||
[Serialize(100.0f, true, description: "The range of the emitted light. Higher values are more performance-intensive.", alwaysUseInstanceValues: true),
|
||||
[Serialize(100.0f, IsPropertySaveable.Yes, description: "The range of the emitted light. Higher values are more performance-intensive.", alwaysUseInstanceValues: true),
|
||||
Editable(MinValueFloat = 0.0f, MaxValueFloat = 2048.0f)]
|
||||
public float Range
|
||||
{
|
||||
@@ -56,8 +56,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize(true, true, description: "Should structures cast shadows when light from this light source hits them. " +
|
||||
"Disabling shadows increases the performance of the game, and is recommended for lights with a short range.", alwaysUseInstanceValues: true)]
|
||||
[Editable, Serialize(true, IsPropertySaveable.Yes, description: "Should structures cast shadows when light from this light source hits them. " +
|
||||
"Disabling shadows increases the performance of the game, and is recommended for lights with a short range. Lights that are set to be drawn behind subs don't cast shadows, regardless of this setting.", alwaysUseInstanceValues: true)]
|
||||
public bool CastShadows
|
||||
{
|
||||
get { return castShadows; }
|
||||
@@ -70,7 +70,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, true, description: "Lights drawn behind submarines don't cast any shadows and are much faster to draw than shadow-casting lights. " +
|
||||
[Editable, Serialize(false, IsPropertySaveable.Yes, description: "Lights drawn behind submarines don't cast any shadows and are much faster to draw than shadow-casting lights. " +
|
||||
"It's recommended to enable this on decorative lights outside the submarine's hull.", alwaysUseInstanceValues: true)]
|
||||
public bool DrawBehindSubs
|
||||
{
|
||||
@@ -84,7 +84,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, true, description: "Is the light currently on.", alwaysUseInstanceValues: true)]
|
||||
[Editable, Serialize(false, IsPropertySaveable.Yes, description: "Is the light currently on.", alwaysUseInstanceValues: true)]
|
||||
public bool IsOn
|
||||
{
|
||||
get { return isOn; }
|
||||
@@ -98,7 +98,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize(0.0f, false, description: "How heavily the light flickers. 0 = no flickering, 1 = the light will alternate between completely dark and full brightness.")]
|
||||
[Editable, Serialize(0.0f, IsPropertySaveable.No, description: "How heavily the light flickers. 0 = no flickering, 1 = the light will alternate between completely dark and full brightness.")]
|
||||
public float Flicker
|
||||
{
|
||||
get { return flicker; }
|
||||
@@ -111,7 +111,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize(1.0f, false, description: "How fast the light flickers.")]
|
||||
[Editable, Serialize(1.0f, IsPropertySaveable.No, description: "How fast the light flickers.")]
|
||||
public float FlickerSpeed
|
||||
{
|
||||
get { return flickerSpeed; }
|
||||
@@ -124,7 +124,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize(0.0f, true, description: "How rapidly the light pulsates (in Hz). 0 = no blinking.")]
|
||||
[Editable, Serialize(0.0f, IsPropertySaveable.Yes, description: "How rapidly the light pulsates (in Hz). 0 = no blinking.")]
|
||||
public float PulseFrequency
|
||||
{
|
||||
get { return pulseFrequency; }
|
||||
@@ -137,7 +137,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f, DecimalCount = 2), Serialize(0.0f, true, description: "How much light pulsates (in Hz). 0 = not at all, 1 = alternates between full brightness and off.")]
|
||||
[Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f, DecimalCount = 2), Serialize(0.0f, IsPropertySaveable.Yes, description: "How much light pulsates (in Hz). 0 = not at all, 1 = alternates between full brightness and off.")]
|
||||
public float PulseAmount
|
||||
{
|
||||
get { return pulseAmount; }
|
||||
@@ -150,7 +150,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize(0.0f, true, description: "How rapidly the light blinks on and off (in Hz). 0 = no blinking.")]
|
||||
[Editable, Serialize(0.0f, IsPropertySaveable.Yes, description: "How rapidly the light blinks on and off (in Hz). 0 = no blinking.")]
|
||||
public float BlinkFrequency
|
||||
{
|
||||
get { return blinkFrequency; }
|
||||
@@ -163,7 +163,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[InGameEditable(FallBackTextTag = "connection.setcolor"), Serialize("255,255,255,255", true, description: "The color of the emitted light (R,G,B,A).", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable(FallBackTextTag = "connection.setcolor"), Serialize("255,255,255,255", IsPropertySaveable.Yes, description: "The color of the emitted light (R,G,B,A).", alwaysUseInstanceValues: true)]
|
||||
public Color LightColor
|
||||
{
|
||||
get { return lightColor; }
|
||||
@@ -179,7 +179,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "If enabled, the component will ignore continuous signals received in the toggle input (i.e. a continuous signal will only toggle it once).")]
|
||||
[Serialize(false, IsPropertySaveable.No, description: "If enabled, the component will ignore continuous signals received in the toggle input (i.e. a continuous signal will only toggle it once).")]
|
||||
public bool IgnoreContinuousToggle
|
||||
{
|
||||
get;
|
||||
@@ -208,7 +208,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public LightComponent(Item item, XElement element)
|
||||
public LightComponent(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
#if CLIENT
|
||||
@@ -264,8 +264,6 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
UpdateOnActiveEffects(deltaTime);
|
||||
|
||||
if (powerIn == null && powerConsumption > 0.0f) { Voltage -= deltaTime; }
|
||||
|
||||
#if CLIENT
|
||||
Light.ParentSub = item.Submarine;
|
||||
#endif
|
||||
@@ -284,7 +282,7 @@ namespace Barotrauma.Items.Components
|
||||
return;
|
||||
}
|
||||
|
||||
currPowerConsumption = powerConsumption;
|
||||
//currPowerConsumption = powerConsumption;
|
||||
if (Rand.Range(0.0f, 1.0f) < 0.05f && Voltage < Rand.Range(0.0f, MinVoltage))
|
||||
{
|
||||
#if CLIENT
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Barotrauma.Items.Components
|
||||
partial class MemoryComponent : ItemComponent, IServerSerializable
|
||||
{
|
||||
private int maxValueLength;
|
||||
[Editable, Serialize(200, false, description: "The maximum length of the stored value. Warning: Large values can lead to large memory usage or networking issues.")]
|
||||
[Editable, Serialize(200, IsPropertySaveable.No, description: "The maximum length of the stored value. Warning: Large values can lead to large memory usage or networking issues.")]
|
||||
public int MaxValueLength
|
||||
{
|
||||
get { return maxValueLength; }
|
||||
@@ -19,7 +19,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private string value;
|
||||
|
||||
[InGameEditable, Serialize("", true, description: "The currently stored signal the item outputs.", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize("", IsPropertySaveable.Yes, description: "The currently stored signal the item outputs.", alwaysUseInstanceValues: true)]
|
||||
public string Value
|
||||
{
|
||||
get { return value; }
|
||||
@@ -34,14 +34,14 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize(true, true, description: "Can the value stored in the memory component be changed via signals.", alwaysUseInstanceValues: true)]
|
||||
[Editable, Serialize(true, IsPropertySaveable.Yes, description: "Can the value stored in the memory component be changed via signals.", alwaysUseInstanceValues: true)]
|
||||
public bool Writeable
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public MemoryComponent(Item item, XElement element)
|
||||
public MemoryComponent(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Barotrauma.Items.Components
|
||||
class ModuloComponent : ItemComponent
|
||||
{
|
||||
private float modulus;
|
||||
[InGameEditable, Serialize(1.0f, false, description: "The modulus of the operation. Must be non-zero.", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize(1.0f, IsPropertySaveable.No, description: "The modulus of the operation. Must be non-zero.", alwaysUseInstanceValues: true)]
|
||||
public float Modulus
|
||||
{
|
||||
get { return modulus; }
|
||||
@@ -16,7 +16,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public ModuloComponent(Item item, XElement element) : base(item, element)
|
||||
public ModuloComponent(Item item, ContentXElement element) : base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
@@ -22,17 +21,17 @@ namespace Barotrauma.Items.Components
|
||||
Wall
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "Has the item currently detected movement. Intended to be used by StatusEffect conditionals (setting this value in XML has no effect).")]
|
||||
[Serialize(false, IsPropertySaveable.No, description: "Has the item currently detected movement. Intended to be used by StatusEffect conditionals (setting this value in XML has no effect).")]
|
||||
public bool MotionDetected { get; set; }
|
||||
|
||||
[InGameEditable, Serialize(TargetType.Any, true, description: "Which kind of targets can trigger the sensor?", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize(TargetType.Any, IsPropertySaveable.Yes, description: "Which kind of targets can trigger the sensor?", alwaysUseInstanceValues: true)]
|
||||
public TargetType Target
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[InGameEditable, Serialize(false, true, description: "Should the sensor ignore the bodies of dead characters?", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize(false, IsPropertySaveable.Yes, description: "Should the sensor ignore the bodies of dead characters?", alwaysUseInstanceValues: true)]
|
||||
public bool IgnoreDead
|
||||
{
|
||||
get;
|
||||
@@ -40,7 +39,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
|
||||
[InGameEditable, Serialize(0.0f, true, description: "Horizontal detection range.", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize(0.0f, IsPropertySaveable.Yes, description: "Horizontal detection range.", alwaysUseInstanceValues: true)]
|
||||
public float RangeX
|
||||
{
|
||||
get { return rangeX; }
|
||||
@@ -52,7 +51,7 @@ namespace Barotrauma.Items.Components
|
||||
#endif
|
||||
}
|
||||
}
|
||||
[InGameEditable, Serialize(0.0f, true, description: "Vertical movement detection range.", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize(0.0f, IsPropertySaveable.Yes, description: "Vertical movement detection range.", alwaysUseInstanceValues: true)]
|
||||
public float RangeY
|
||||
{
|
||||
get { return rangeY; }
|
||||
@@ -62,7 +61,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[InGameEditable, Serialize("0,0", true, description: "The position to detect the movement at relative to the item. For example, 0,100 would detect movement 100 units above the item.")]
|
||||
[InGameEditable, Serialize("0,0", IsPropertySaveable.Yes, description: "The position to detect the movement at relative to the item. For example, 0,100 would detect movement 100 units above the item.")]
|
||||
public Vector2 DetectOffset
|
||||
{
|
||||
get { return detectOffset; }
|
||||
@@ -85,7 +84,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Editable(MinValueFloat = 0.1f, MaxValueFloat = 100.0f, DecimalCount = 2), Serialize(0.1f, true, description: "How often the sensor checks if there's something moving near it. Higher values are better for performance.", alwaysUseInstanceValues: true)]
|
||||
[Editable(MinValueFloat = 0.1f, MaxValueFloat = 100.0f, DecimalCount = 2), Serialize(0.1f, IsPropertySaveable.Yes, description: "How often the sensor checks if there's something moving near it. Higher values are better for performance.", alwaysUseInstanceValues: true)]
|
||||
public float UpdateInterval
|
||||
{
|
||||
get;
|
||||
@@ -93,7 +92,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
private int maxOutputLength;
|
||||
[Editable, Serialize(200, false, description: "The maximum length of the output strings. Warning: Large values can lead to large memory usage or networking issues.")]
|
||||
[Editable, Serialize(200, IsPropertySaveable.No, description: "The maximum length of the output strings. Warning: Large values can lead to large memory usage or networking issues.")]
|
||||
public int MaxOutputLength
|
||||
{
|
||||
get { return maxOutputLength; }
|
||||
@@ -104,7 +103,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
private string output;
|
||||
[InGameEditable, Serialize("1", true, description: "The signal the item outputs when it has detected movement.", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize("1", IsPropertySaveable.Yes, description: "The signal the item outputs when it has detected movement.", alwaysUseInstanceValues: true)]
|
||||
public string Output
|
||||
{
|
||||
get { return output; }
|
||||
@@ -120,7 +119,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
private string falseOutput;
|
||||
[InGameEditable, Serialize("", true, description: "The signal the item outputs when it has not detected movement.", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize("", IsPropertySaveable.Yes, description: "The signal the item outputs when it has not detected movement.", alwaysUseInstanceValues: true)]
|
||||
public string FalseOutput
|
||||
{
|
||||
get { return falseOutput; }
|
||||
@@ -135,27 +134,27 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Editable(DecimalCount = 3), Serialize(0.01f, true, description: "How fast the objects within the detector's range have to be moving (in m/s).", alwaysUseInstanceValues: true)]
|
||||
[Editable(DecimalCount = 3), Serialize(0.01f, IsPropertySaveable.Yes, description: "How fast the objects within the detector's range have to be moving (in m/s).", alwaysUseInstanceValues: true)]
|
||||
public float MinimumVelocity
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(true, true, description: "Should the sensor trigger when the item itself moves.")]
|
||||
[Serialize(true, IsPropertySaveable.Yes, description: "Should the sensor trigger when the item itself moves.")]
|
||||
public bool DetectOwnMotion
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public MotionSensor(Item item, XElement element)
|
||||
public MotionSensor(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
|
||||
//backwards compatibility
|
||||
if (element.Attribute("range") != null)
|
||||
if (element.GetAttribute("range") != null)
|
||||
{
|
||||
rangeX = rangeY = element.GetAttributeFloat("range", 0.0f);
|
||||
}
|
||||
@@ -164,7 +163,7 @@ namespace Barotrauma.Items.Components
|
||||
updateTimer = Rand.Range(0.0f, UpdateInterval);
|
||||
}
|
||||
|
||||
public override void Load(XElement componentElement, bool usePrefabValues, IdRemap idRemap)
|
||||
public override void Load(ContentXElement componentElement, bool usePrefabValues, IdRemap idRemap)
|
||||
{
|
||||
base.Load(componentElement, usePrefabValues, idRemap);
|
||||
//backwards compatibility
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
class MultiplyComponent : ArithmeticComponent
|
||||
{
|
||||
public MultiplyComponent(Item item, XElement element)
|
||||
public MultiplyComponent(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class NotComponent : ItemComponent
|
||||
{
|
||||
private bool signalReceived;
|
||||
|
||||
private bool continuousOutput;
|
||||
[Editable, Serialize(false, true, description: "When enabled, the component continuously outputs \"1\" when it's not receiving a signal.", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize(false, IsPropertySaveable.Yes, description: "When enabled, the component continuously outputs \"1\" when it's not receiving a signal.", alwaysUseInstanceValues: true)]
|
||||
public bool ContinuousOutput
|
||||
{
|
||||
get { return continuousOutput; }
|
||||
set { continuousOutput = IsActive = value; }
|
||||
}
|
||||
|
||||
public NotComponent(Item item, XElement element)
|
||||
public NotComponent(Item item, ContentXElement element)
|
||||
: base (item, element)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
class OrComponent : AndComponent
|
||||
{
|
||||
public OrComponent(Item item, XElement element)
|
||||
public OrComponent(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
|
||||
+3
-3
@@ -22,7 +22,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private float phase;
|
||||
|
||||
[InGameEditable, Serialize(WaveType.Pulse, true, description: "What kind of a signal the item outputs." +
|
||||
[InGameEditable, Serialize(WaveType.Pulse, IsPropertySaveable.Yes, description: "What kind of a signal the item outputs." +
|
||||
" Pulse: periodically sends out a signal of 1." +
|
||||
" Sawtooth: sends out a periodic wave that increases linearly from 0 to 1." +
|
||||
" Sine: sends out a sine wave oscillating between -1 and 1." +
|
||||
@@ -35,7 +35,7 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[InGameEditable(DecimalCount = 2), Serialize(1.0f, true, description: "How fast the signal oscillates, or how fast the pulses are sent (in Hz).", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable(DecimalCount = 2), Serialize(1.0f, IsPropertySaveable.Yes, description: "How fast the signal oscillates, or how fast the pulses are sent (in Hz).", alwaysUseInstanceValues: true)]
|
||||
public float Frequency
|
||||
{
|
||||
get { return frequency; }
|
||||
@@ -47,7 +47,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public OscillatorComponent(Item item, XElement element) :
|
||||
public OscillatorComponent(Item item, ContentXElement element) :
|
||||
base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
|
||||
@@ -4,10 +4,12 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
class OxygenDetector : ItemComponent
|
||||
{
|
||||
public const int LowOxygenPercentage = 35;
|
||||
|
||||
private int prevSentOxygenValue;
|
||||
private string oxygenSignal;
|
||||
|
||||
public OxygenDetector(Item item, XElement element)
|
||||
public OxygenDetector(Item item, ContentXElement element)
|
||||
: base (item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
@@ -17,13 +19,15 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (item.CurrentHull == null) { return; }
|
||||
|
||||
if (prevSentOxygenValue != (int)item.CurrentHull.OxygenPercentage || oxygenSignal == null)
|
||||
int currOxygenPercentage = (int)item.CurrentHull.OxygenPercentage;
|
||||
if (prevSentOxygenValue != currOxygenPercentage || oxygenSignal == null)
|
||||
{
|
||||
prevSentOxygenValue = (int)item.CurrentHull.OxygenPercentage;
|
||||
prevSentOxygenValue = currOxygenPercentage;
|
||||
oxygenSignal = prevSentOxygenValue.ToString();
|
||||
}
|
||||
|
||||
item.SendSignal(oxygenSignal, "signal_out");
|
||||
item.SendSignal(oxygenSignal, "signal_out");
|
||||
item.SendSignal(currOxygenPercentage <= LowOxygenPercentage ? "1" : "0", "low_oxygen");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+7
-7
@@ -21,7 +21,7 @@ namespace Barotrauma.Items.Components
|
||||
private bool nonContinuousOutputSent;
|
||||
|
||||
private int maxOutputLength;
|
||||
[Editable, Serialize(200, false, description: "The maximum length of the output string. Warning: Large values can lead to large memory usage or networking issues.")]
|
||||
[Editable, Serialize(200, IsPropertySaveable.No, description: "The maximum length of the output string. Warning: Large values can lead to large memory usage or networking issues.")]
|
||||
public int MaxOutputLength
|
||||
{
|
||||
get { return maxOutputLength; }
|
||||
@@ -33,7 +33,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private string output;
|
||||
|
||||
[InGameEditable, Serialize("1", true, description: "The signal this item outputs when the received signal matches the regular expression.", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize("1", IsPropertySaveable.Yes, description: "The signal this item outputs when the received signal matches the regular expression.", alwaysUseInstanceValues: true)]
|
||||
public string Output
|
||||
{
|
||||
get { return output; }
|
||||
@@ -48,16 +48,16 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[InGameEditable, Serialize(false, true, description: "Should the component output a value of a capture group instead of a constant signal.", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize(false, IsPropertySaveable.Yes, description: "Should the component output a value of a capture group instead of a constant signal.", alwaysUseInstanceValues: true)]
|
||||
public bool UseCaptureGroup { get; set; }
|
||||
|
||||
[InGameEditable, Serialize("0", true, description: "The signal this item outputs when the received signal does not match the regular expression.", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize("0", IsPropertySaveable.Yes, description: "The signal this item outputs when the received signal does not match the regular expression.", alwaysUseInstanceValues: true)]
|
||||
public string FalseOutput { get; set; }
|
||||
|
||||
[InGameEditable, Serialize(true, true, description: "Should the component keep sending the output even after it stops receiving a signal, or only send an output when it receives a signal.", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize(true, IsPropertySaveable.Yes, description: "Should the component keep sending the output even after it stops receiving a signal, or only send an output when it receives a signal.", alwaysUseInstanceValues: true)]
|
||||
public bool ContinuousOutput { get; set; }
|
||||
|
||||
[InGameEditable, Serialize("", true, description: "The regular expression used to check the incoming signals.", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize("", IsPropertySaveable.Yes, description: "The regular expression used to check the incoming signals.", alwaysUseInstanceValues: true)]
|
||||
public string Expression
|
||||
{
|
||||
get { return expression; }
|
||||
@@ -82,7 +82,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public RegExFindComponent(Item item, XElement element)
|
||||
public RegExFindComponent(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
nonContinuousOutputSent = true;
|
||||
|
||||
+229
-62
@@ -13,7 +13,23 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private bool isOn;
|
||||
|
||||
private float throttlePowerOutput;
|
||||
private float prevVoltage;
|
||||
|
||||
private float? newVoltage = null;
|
||||
|
||||
//internal load buffer that's used to isolate the input and output sides from each other
|
||||
private float internalLoadBuffer = 0;
|
||||
|
||||
//previous load (used to smooth changes in the load to prevent oscillations)
|
||||
private float prevInternalLoad = 0;
|
||||
|
||||
//previous load on the output side
|
||||
private float prevExternalLoad = 0;
|
||||
|
||||
//difference between the internal load buffer and external load
|
||||
private float bufferDiff = 0;
|
||||
|
||||
private float thirdInverseMax = 0, loadEqnConstant = 0;
|
||||
|
||||
private static readonly Dictionary<string, string> connectionPairs = new Dictionary<string, string>
|
||||
{
|
||||
@@ -25,19 +41,36 @@ namespace Barotrauma.Items.Components
|
||||
{ "signal_in4", "signal_out4" },
|
||||
{ "signal_in5", "signal_out5" }
|
||||
};
|
||||
public float DisplayLoad { get; set; }
|
||||
|
||||
[Editable, Serialize(1000.0f, true, description: "The maximum amount of power that can pass through the item.")]
|
||||
protected override PowerPriority Priority { get { return PowerPriority.Relay; } }
|
||||
|
||||
public float DisplayLoad
|
||||
{
|
||||
get
|
||||
{
|
||||
if (powerOut != null && powerOut.Grid != null)
|
||||
{
|
||||
return powerOut.Grid.Load;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize(1000.0f, IsPropertySaveable.Yes, description: "The maximum amount of power that can pass through the item.")]
|
||||
public float MaxPower
|
||||
{
|
||||
get { return maxPower; }
|
||||
set
|
||||
{
|
||||
maxPower = Math.Max(0.0f, value);
|
||||
SetLoadFormulaValues();
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize(true, true, description: "Can the relay currently pass power and signals through it.", alwaysUseInstanceValues: true)]
|
||||
[Editable, Serialize(true, IsPropertySaveable.Yes, description: "Can the relay currently pass power and signals through it.", alwaysUseInstanceValues: true)]
|
||||
public bool IsOn
|
||||
{
|
||||
get
|
||||
@@ -55,13 +88,23 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public RelayComponent(Item item, XElement element)
|
||||
public RelayComponent(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
throttlePowerOutput = MaxPower;
|
||||
prevVoltage = 0;
|
||||
SetLoadFormulaValues();
|
||||
}
|
||||
|
||||
private void SetLoadFormulaValues()
|
||||
{
|
||||
internalLoadBuffer = MaxPower * 2;
|
||||
// Set constants for load Formula to reduce calculation time
|
||||
thirdInverseMax = 1 / (3 * maxPower);
|
||||
loadEqnConstant = (8 * maxPower) / 3;
|
||||
}
|
||||
|
||||
|
||||
public override void OnItemLoaded()
|
||||
{
|
||||
base.OnItemLoaded();
|
||||
@@ -87,8 +130,8 @@ namespace Barotrauma.Items.Components
|
||||
RefreshConnections();
|
||||
|
||||
item.SendSignal(IsOn ? "1" : "0", "state_out");
|
||||
|
||||
if (!CanTransfer) { Voltage = 0.0f; return; }
|
||||
item.SendSignal(((int)Math.Round(-PowerLoad)).ToString(), "power_value_out");
|
||||
item.SendSignal(((int)Math.Round(DisplayLoad)).ToString(), "load_value_out");
|
||||
|
||||
if (isBroken)
|
||||
{
|
||||
@@ -98,75 +141,199 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
|
||||
if (powerOut != null)
|
||||
{
|
||||
bool overloaded = false;
|
||||
foreach (Connection recipient in powerOut.Recipients)
|
||||
{
|
||||
var pt = recipient.Item.GetComponent<PowerTransfer>();
|
||||
if (pt != null)
|
||||
{
|
||||
float overload = -pt.CurrPowerConsumption - pt.PowerLoad;
|
||||
throttlePowerOutput += overload * deltaTime * 0.5f;
|
||||
overloaded = overload > 1.0f;
|
||||
}
|
||||
}
|
||||
throttlePowerOutput = overloaded ?
|
||||
MathHelper.Clamp(throttlePowerOutput, 0.0f, MaxPower):
|
||||
Math.Max(throttlePowerOutput - MaxPower * 0.1f * deltaTime, 0.0f);
|
||||
}
|
||||
|
||||
if (Math.Min(-currPowerConsumption, PowerLoad) > maxPower && CanBeOverloaded)
|
||||
if (Voltage > OverloadVoltage && CanBeOverloaded)
|
||||
{
|
||||
item.Condition = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
public override void ReceivePowerProbeSignal(Connection connection, Item source, float power)
|
||||
/// <summary>
|
||||
/// Relay power consumption. Load consumption is based on the internal buffer.
|
||||
/// This allows for the relay to react to demand and find equilibrium in loop configurations.
|
||||
/// </summary>
|
||||
public override float GetCurrentPowerConsumption(Connection connection = null)
|
||||
{
|
||||
if (!IsOn || item.Condition <= 0.0f) { return; }
|
||||
|
||||
//we've already received this signal
|
||||
if (lastPowerProbeRecipients.Contains(this)) { return; }
|
||||
lastPowerProbeRecipients.Add(this);
|
||||
|
||||
if (power < 0.0f)
|
||||
//Can't output or draw if broken
|
||||
if (isBroken)
|
||||
{
|
||||
if (!connection.IsOutput || powerIn == null) { return; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
//power being drawn from the power_out connection
|
||||
DisplayLoad -= Math.Min(power, 0.0f);
|
||||
powerLoad -= Math.Min(power + throttlePowerOutput, 0.0f);
|
||||
if (connection == powerIn)
|
||||
{
|
||||
float currentLoad = MaxPower;
|
||||
if (internalLoadBuffer > MaxPower)
|
||||
{
|
||||
//Buffer load charging curve - special relay sauce
|
||||
// Original formula (buffer - 3 * maxPower)^2 / (3 * maxPower) - (maxPower / 3)
|
||||
//loadDraw = MathHelper.Clamp((float)Math.Pow(internalBuffer - 3 * MaxPower, 2) / (3 * MaxPower) - MaxPower / 3, 1, MaxPower);
|
||||
//Optimised formula 0.2% error from original
|
||||
currentLoad = MathHelper.Clamp(internalLoadBuffer * internalLoadBuffer * thirdInverseMax - 2 * internalLoadBuffer + loadEqnConstant, 0.001f, MaxPower);
|
||||
|
||||
//pass the load to items connected to the input
|
||||
powerIn.SendPowerProbeSignal(source, Math.Max(power, -MaxPower));
|
||||
//Slight smoothing to load to minimise relay jank
|
||||
currentLoad = MathHelper.Clamp((currentLoad + prevInternalLoad * 0.1f) / 1.1f, 0.001f, MaxPower);
|
||||
prevInternalLoad = currentLoad;
|
||||
}
|
||||
|
||||
//Add on extra load after calculation
|
||||
currentLoad += ExtraLoad;
|
||||
return currentLoad;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (connection.IsOutput || powerOut == null) { return; }
|
||||
//power being supplied to the power_in connection
|
||||
if (currPowerConsumption - power < -MaxPower)
|
||||
//Flag output as power out
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private bool RelayCanOutput()
|
||||
{
|
||||
//Only allow output if device is on, buffers have charge and the connected grids aren't short circuited
|
||||
return isOn && powerIn != null && powerIn.Grid != null && internalLoadBuffer > 0 && powerOut != null && powerOut.Grid != null && powerIn.Grid != powerOut.Grid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Minimum and maximum power out for the relay.
|
||||
/// Max out is adjusted to allow for other relays to compensate if this relay is undervolted.
|
||||
/// </summary>
|
||||
public override PowerRange MinMaxPowerOut(Connection connection, float load = 0)
|
||||
{
|
||||
if (connection == powerOut)
|
||||
{
|
||||
if (RelayCanOutput())
|
||||
{
|
||||
power += MaxPower + (currPowerConsumption - power);
|
||||
}
|
||||
//Determine output limits from buffer and voltage
|
||||
float bufferDraw = MathHelper.Min(internalLoadBuffer, MaxPower);
|
||||
float voltageLimit = MathHelper.Min(MaxPower, load) * prevVoltage;
|
||||
|
||||
currPowerConsumption -= power;
|
||||
//If undervolted adjust max output so that other relays can compensate
|
||||
if (prevVoltage < 1)
|
||||
{
|
||||
voltageLimit *= prevVoltage;
|
||||
}
|
||||
|
||||
foreach (Connection recipient in powerOut.Recipients)
|
||||
{
|
||||
if (!recipient.IsPower) { continue; }
|
||||
var powered = recipient.Item.GetComponent<Powered>();
|
||||
if (powered == null) { continue; }
|
||||
|
||||
float load = powered.CurrPowerConsumption;
|
||||
var powerTransfer = powered as PowerTransfer;
|
||||
if (powerTransfer != null) { load = powerTransfer.PowerLoad; }
|
||||
|
||||
float powerOut = power * (load / Math.Max(powerLoad + throttlePowerOutput, 0.01f));
|
||||
powered.ReceivePowerProbeSignal(recipient, source, Math.Min(powerOut, power));
|
||||
float maxOutput = MathHelper.Min(voltageLimit, bufferDraw);
|
||||
return new PowerRange(0.0f, maxOutput);
|
||||
}
|
||||
}
|
||||
|
||||
return PowerRange.Zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Power out for the relay connection.
|
||||
/// Relay will output the necessary power to the grid based on maximum power output of other
|
||||
/// relays and will undervolt and overvolt the grid following its supply grid.
|
||||
/// </summary>
|
||||
/// <returns>Power outputted to the grid</returns>
|
||||
public override float GetConnectionPowerOut(Connection connection, float power, PowerRange minMaxPower, float load)
|
||||
{
|
||||
if (connection == powerIn)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
else if (RelayCanOutput())
|
||||
{
|
||||
//Determine output limits of the relay
|
||||
float bufferDraw = MathHelper.Min(internalLoadBuffer, MaxPower);
|
||||
float voltageLimit = MathHelper.Min(MaxPower, load) * prevVoltage;
|
||||
float maxOut = MathHelper.Min(voltageLimit, bufferDraw);
|
||||
|
||||
//Don't output negative power to the grid
|
||||
if (maxOut < 0)
|
||||
{
|
||||
PowerLoad = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
prevExternalLoad = load;
|
||||
|
||||
//Calculate power out
|
||||
PowerLoad = MathHelper.Clamp((load * prevVoltage - power) / MathHelper.Max(minMaxPower.Max, 1E-20f) * -maxOut, -maxOut, 0);
|
||||
return -PowerLoad;
|
||||
}
|
||||
|
||||
//Else relay isn't outputting
|
||||
PowerLoad = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connection's grid resolved, determine the difference to be added to the buffer.
|
||||
/// Ensure the prevVoltage voltage is updated once both grids are resolved.
|
||||
/// </summary>
|
||||
public override void GridResolved(Connection conn)
|
||||
{
|
||||
if (conn == powerIn)
|
||||
{
|
||||
if (powerIn != null && powerIn.Grid != null)
|
||||
{
|
||||
float addToBuffer = powerIn.Grid.Voltage * (CurrPowerConsumption - ExtraLoad);
|
||||
|
||||
//Limit power input to the previous voltage to prevent wild oscillations in overload
|
||||
if (powerIn.Grid.Voltage > 1)
|
||||
{
|
||||
addToBuffer = prevVoltage * (CurrPowerConsumption - ExtraLoad);
|
||||
}
|
||||
|
||||
//Cap the max power input
|
||||
if (addToBuffer > MaxPower)
|
||||
{
|
||||
addToBuffer = MaxPower;
|
||||
}
|
||||
|
||||
//To prevent problems with grid order, only update voltage and buffer after input and output grids have been resolved
|
||||
if (newVoltage == null)
|
||||
{
|
||||
//temporarily store the new voltage and also indicates that the input connection side has been updated
|
||||
newVoltage = powerIn.Grid.Voltage;
|
||||
bufferDiff = addToBuffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateBuffer(addToBuffer, powerIn.Grid.Voltage);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//To prevent problems with grid order, only update voltage and buffer after input and output grids have been resolved
|
||||
if (newVoltage == null)
|
||||
{
|
||||
//Flag that output connection has been updated already
|
||||
newVoltage = -1;
|
||||
bufferDiff = PowerLoad;
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateBuffer(PowerLoad, (float)newVoltage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateBuffer(float addToBuffer, float newVoltage)
|
||||
{
|
||||
//Update buffer and voltage
|
||||
float limit = MaxPower * 2;
|
||||
|
||||
//Clamp the buffer to have a constant load in a severe overload event, otherwise wild oscillation will occur
|
||||
if (RelayCanOutput() && powerIn.Grid.Voltage > 2)
|
||||
{
|
||||
limit = MathHelper.Min(limit, 3 * MaxPower - (float)Math.Sqrt((3 * prevExternalLoad + MaxPower) * MaxPower));
|
||||
}
|
||||
|
||||
//Add to the internal buffer
|
||||
internalLoadBuffer = MathHelper.Clamp(internalLoadBuffer + bufferDiff + addToBuffer, 0, limit);
|
||||
|
||||
//Decay overvoltage slightly, helps resolve large chain loops to a grid
|
||||
if (newVoltage > 1)
|
||||
{
|
||||
newVoltage = MathHelper.Max(newVoltage - 0.0005f, 1);
|
||||
}
|
||||
|
||||
prevVoltage = newVoltage;
|
||||
this.newVoltage = null;
|
||||
bufferDiff = 0;
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(Signal signal, Connection connection)
|
||||
@@ -192,7 +359,7 @@ namespace Barotrauma.Items.Components
|
||||
public void SetState(bool on, bool isNetworkMessage)
|
||||
{
|
||||
#if CLIENT
|
||||
if (GameMain.Client != null && !isNetworkMessage) return;
|
||||
if (GameMain.Client != null && !isNetworkMessage) { return; }
|
||||
#endif
|
||||
|
||||
#if SERVER
|
||||
@@ -205,12 +372,12 @@ namespace Barotrauma.Items.Components
|
||||
IsOn = on;
|
||||
}
|
||||
|
||||
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
|
||||
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
msg.Write(isOn);
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
SetState(msg.ReadBoolean(), true);
|
||||
}
|
||||
|
||||
+5
-5
@@ -6,7 +6,7 @@ namespace Barotrauma.Items.Components
|
||||
class SignalCheckComponent : ItemComponent
|
||||
{
|
||||
private int maxOutputLength;
|
||||
[Editable, Serialize(200, false, description: "The maximum length of the output strings. Warning: Large values can lead to large memory usage or networking issues.")]
|
||||
[Editable, Serialize(200, IsPropertySaveable.No, description: "The maximum length of the output strings. Warning: Large values can lead to large memory usage or networking issues.")]
|
||||
public int MaxOutputLength
|
||||
{
|
||||
get { return maxOutputLength; }
|
||||
@@ -17,7 +17,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
private string output;
|
||||
[InGameEditable, Serialize("1", true, description: "The signal this item outputs when the received signal matches the target signal.", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize("1", IsPropertySaveable.Yes, description: "The signal this item outputs when the received signal matches the target signal.", alwaysUseInstanceValues: true)]
|
||||
public string Output
|
||||
{
|
||||
get { return output; }
|
||||
@@ -33,7 +33,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
private string falseOutput;
|
||||
[InGameEditable, Serialize("0", true, description: "The signal this item outputs when the received signal does not match the target signal.", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize("0", IsPropertySaveable.Yes, description: "The signal this item outputs when the received signal does not match the target signal.", alwaysUseInstanceValues: true)]
|
||||
public string FalseOutput
|
||||
{
|
||||
get { return falseOutput; }
|
||||
@@ -48,10 +48,10 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[InGameEditable, Serialize("", true, description: "The value to compare the received signals against.", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize("", IsPropertySaveable.Yes, description: "The value to compare the received signals against.", alwaysUseInstanceValues: true)]
|
||||
public string TargetSignal { get; set; }
|
||||
|
||||
public SignalCheckComponent(Item item, XElement element)
|
||||
public SignalCheckComponent(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Barotrauma.Items.Components
|
||||
private bool fireInRange;
|
||||
|
||||
private int maxOutputLength;
|
||||
[Editable, Serialize(200, false, description: "The maximum length of the output strings. Warning: Large values can lead to large memory usage or networking issues.")]
|
||||
[Editable, Serialize(200, IsPropertySaveable.No, description: "The maximum length of the output strings. Warning: Large values can lead to large memory usage or networking issues.")]
|
||||
public int MaxOutputLength
|
||||
{
|
||||
get { return maxOutputLength; }
|
||||
@@ -22,7 +22,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
private string output;
|
||||
[InGameEditable, Serialize("1", true, description: "The signal the item outputs when it has detected a fire.", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize("1", IsPropertySaveable.Yes, description: "The signal the item outputs when it has detected a fire.", alwaysUseInstanceValues: true)]
|
||||
public string Output
|
||||
{
|
||||
get { return output; }
|
||||
@@ -38,7 +38,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
private string falseOutput;
|
||||
[InGameEditable, Serialize("0", true, description: "The signal the item outputs when it has not detected a fire.", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize("0", IsPropertySaveable.Yes, description: "The signal the item outputs when it has not detected a fire.", alwaysUseInstanceValues: true)]
|
||||
public string FalseOutput
|
||||
{
|
||||
get { return falseOutput; }
|
||||
@@ -53,7 +53,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public SmokeDetector(Item item, XElement element)
|
||||
public SmokeDetector(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
@@ -68,7 +68,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
foreach (FireSource fireSource in hull.FireSources)
|
||||
{
|
||||
if (fireSource.IsInDamageRange(item.WorldPosition, fireSource.DamageRange * 2.0f)) { return true; }
|
||||
if (fireSource.IsInDamageRange(item.WorldPosition, Math.Max(fireSource.DamageRange * 2.0f, 500.0f))) { return true; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Xml.Linq;
|
||||
using System;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -17,7 +14,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
|
||||
[InGameEditable(DecimalCount = 2),
|
||||
Serialize(0.0f, true, description: "The item must have received signals to both inputs within this timeframe to output the result." +
|
||||
Serialize(0.0f, IsPropertySaveable.Yes, description: "The item must have received signals to both inputs within this timeframe to output the result." +
|
||||
" If set to 0, the inputs must be received at the same time.", alwaysUseInstanceValues: true)]
|
||||
public float TimeFrame
|
||||
{
|
||||
@@ -32,7 +29,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public StringComponent(Item item, XElement element)
|
||||
public StringComponent(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
timeSinceReceived = new float[] { Math.Max(timeFrame * 2.0f, 0.1f), Math.Max(timeFrame * 2.0f, 0.1f) };
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
class SubtractComponent : ArithmeticComponent
|
||||
{
|
||||
public SubtractComponent(Item item, XElement element)
|
||||
public SubtractComponent(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -32,14 +32,14 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private List<TerminalMessage> messageHistory = new List<TerminalMessage>(MaxMessages);
|
||||
|
||||
public string DisplayedWelcomeMessage
|
||||
public LocalizedString DisplayedWelcomeMessage
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
private string welcomeMessage;
|
||||
[InGameEditable, Serialize("", true, "Message to be displayed on the terminal display when it is first opened.", translationTextTag = "terminalwelcomemsg.", AlwaysUseInstanceValues = true)]
|
||||
[InGameEditable, Serialize("", IsPropertySaveable.Yes, "Message to be displayed on the terminal display when it is first opened.", translationTextTag: "terminalwelcomemsg.", alwaysUseInstanceValues: true)]
|
||||
public string WelcomeMessage
|
||||
{
|
||||
get { return welcomeMessage; }
|
||||
@@ -47,7 +47,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (welcomeMessage == value) { return; }
|
||||
welcomeMessage = value;
|
||||
DisplayedWelcomeMessage = TextManager.Get(welcomeMessage, returnNull: true) ?? welcomeMessage.Replace("\\n", "\n");
|
||||
DisplayedWelcomeMessage = TextManager.Get(welcomeMessage).Fallback(welcomeMessage.Replace("\\n", "\n"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,12 +64,12 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, true, description: "The terminal will use a monospace font if this box is ticked.", alwaysUseInstanceValues: true)]
|
||||
[Editable, Serialize(false, IsPropertySaveable.Yes, description: "The terminal will use a monospace font if this box is ticked.", alwaysUseInstanceValues: true)]
|
||||
public bool UseMonospaceFont { get; set; }
|
||||
|
||||
private Color textColor = Color.LimeGreen;
|
||||
|
||||
[Editable, Serialize("50,205,50,255", true, description: "Color of the terminal text.", alwaysUseInstanceValues: true)]
|
||||
[Editable, Serialize("50,205,50,255", IsPropertySaveable.Yes, description: "Color of the terminal text.", alwaysUseInstanceValues: true)]
|
||||
public Color TextColor
|
||||
{
|
||||
get => textColor;
|
||||
@@ -89,7 +89,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private string prevColorSignal;
|
||||
|
||||
public Terminal(Item item, XElement element)
|
||||
public Terminal(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
@@ -143,9 +143,9 @@ namespace Barotrauma.Items.Components
|
||||
#endif
|
||||
|
||||
base.OnItemLoaded();
|
||||
if (!string.IsNullOrEmpty(DisplayedWelcomeMessage))
|
||||
if (!DisplayedWelcomeMessage.IsNullOrEmpty())
|
||||
{
|
||||
ShowOnDisplay(DisplayedWelcomeMessage, addToHistory: !isSubEditor, TextColor);
|
||||
ShowOnDisplay(DisplayedWelcomeMessage.Value, addToHistory: !isSubEditor, TextColor);
|
||||
DisplayedWelcomeMessage = "";
|
||||
//remove welcome message if a game session is running so it doesn't reappear on successive rounds
|
||||
if (GameMain.GameSession != null && !isSubEditor)
|
||||
@@ -166,7 +166,7 @@ namespace Barotrauma.Items.Components
|
||||
return componentElement;
|
||||
}
|
||||
|
||||
public override void Load(XElement componentElement, bool usePrefabValues, IdRemap idRemap)
|
||||
public override void Load(ContentXElement componentElement, bool usePrefabValues, IdRemap idRemap)
|
||||
{
|
||||
base.Load(componentElement, usePrefabValues, idRemap);
|
||||
for (int i = 0; i < MaxMessages; i++)
|
||||
|
||||
+7
-4
@@ -20,21 +20,23 @@ namespace Barotrauma.Items.Components
|
||||
private readonly float[] receivedSignal = new float[2];
|
||||
private readonly float[] timeSinceReceived = new float[2];
|
||||
|
||||
[Serialize(FunctionType.Sin, false, description: "Which kind of function to run the input through.", alwaysUseInstanceValues: true)]
|
||||
protected Character signalSender;
|
||||
|
||||
[Serialize(FunctionType.Sin, IsPropertySaveable.No, description: "Which kind of function to run the input through.", alwaysUseInstanceValues: true)]
|
||||
public FunctionType Function
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
|
||||
[InGameEditable, Serialize(false, true, description: "If set to true, the trigonometric function uses radians instead of degrees.", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize(false, IsPropertySaveable.Yes, description: "If set to true, the trigonometric function uses radians instead of degrees.", alwaysUseInstanceValues: true)]
|
||||
public bool UseRadians
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
|
||||
public TrigonometricFunctionComponent(Item item, XElement element)
|
||||
public TrigonometricFunctionComponent(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
@@ -56,7 +58,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
float angle = (float)Math.Atan2(receivedSignal[1], receivedSignal[0]);
|
||||
if (!UseRadians) { angle = MathHelper.ToDegrees(angle); }
|
||||
item.SendSignal(angle.ToString("G", CultureInfo.InvariantCulture), "signal_out");
|
||||
item.SendSignal(new Signal(angle.ToString("G", CultureInfo.InvariantCulture), sender: signalSender), "signal_out");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,6 +67,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
float.TryParse(signal.value, NumberStyles.Float, CultureInfo.InvariantCulture, out float value);
|
||||
bool sendOutputImmediately = true;
|
||||
signalSender = signal.sender;
|
||||
switch (Function)
|
||||
{
|
||||
case FunctionType.Sin:
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Barotrauma.Items.Components
|
||||
private float stateSwitchDelay;
|
||||
|
||||
private int maxOutputLength;
|
||||
[Editable, Serialize(200, false, description: "The maximum length of the output strings. Warning: Large values can lead to large memory usage or networking issues.")]
|
||||
[Editable, Serialize(200, IsPropertySaveable.No, description: "The maximum length of the output strings. Warning: Large values can lead to large memory usage or networking issues.")]
|
||||
public int MaxOutputLength
|
||||
{
|
||||
get { return maxOutputLength; }
|
||||
@@ -27,7 +27,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
private string output;
|
||||
[InGameEditable, Serialize("1", true, description: "The signal the item sends out when it's underwater.", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize("1", IsPropertySaveable.Yes, description: "The signal the item sends out when it's underwater.", alwaysUseInstanceValues: true)]
|
||||
public string Output
|
||||
{
|
||||
get { return output; }
|
||||
@@ -43,7 +43,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
private string falseOutput;
|
||||
[InGameEditable, Serialize("0", true, description: "The signal the item sends out when it's not underwater.", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize("0", IsPropertySaveable.Yes, description: "The signal the item sends out when it's not underwater.", alwaysUseInstanceValues: true)]
|
||||
public string FalseOutput
|
||||
{
|
||||
get { return falseOutput; }
|
||||
@@ -58,7 +58,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public WaterDetector(Item item, XElement element)
|
||||
public WaterDetector(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
|
||||
@@ -30,10 +30,10 @@ namespace Barotrauma.Items.Components
|
||||
private Connection signalInConnection;
|
||||
private Connection signalOutConnection;
|
||||
|
||||
[Serialize(CharacterTeamType.None, true, description: "WiFi components can only communicate with components that have the same Team ID.", alwaysUseInstanceValues: true)]
|
||||
[Serialize(CharacterTeamType.None, IsPropertySaveable.Yes, description: "WiFi components can only communicate with components that have the same Team ID.", alwaysUseInstanceValues: true)]
|
||||
public CharacterTeamType TeamID { get; set; }
|
||||
|
||||
[Editable, Serialize(20000.0f, false, description: "How close the recipient has to be to receive a signal from this WiFi component.", alwaysUseInstanceValues: true)]
|
||||
[Editable, Serialize(20000.0f, IsPropertySaveable.No, description: "How close the recipient has to be to receive a signal from this WiFi component.", alwaysUseInstanceValues: true)]
|
||||
public float Range
|
||||
{
|
||||
get { return range; }
|
||||
@@ -46,7 +46,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[InGameEditable, Serialize(0, true, description: "WiFi components can only communicate with components that use the same channel.", alwaysUseInstanceValues: true)]
|
||||
[InGameEditable, Serialize(0, IsPropertySaveable.Yes, description: "WiFi components can only communicate with components that use the same channel.", alwaysUseInstanceValues: true)]
|
||||
public int Channel
|
||||
{
|
||||
get { return channel; }
|
||||
@@ -57,7 +57,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
|
||||
[Editable, Serialize(false, true, description: "Can the component communicate with wifi components in another team's submarine (e.g. enemy sub in Combat missions, respawn shuttle). Needs to be enabled on both the component transmitting the signal and the component receiving it.", alwaysUseInstanceValues: true)]
|
||||
[Editable, Serialize(false, IsPropertySaveable.Yes, description: "Can the component communicate with wifi components in another team's submarine (e.g. enemy sub in Combat missions, respawn shuttle). Needs to be enabled on both the component transmitting the signal and the component receiving it.", alwaysUseInstanceValues: true)]
|
||||
public bool AllowCrossTeamCommunication
|
||||
{
|
||||
get;
|
||||
@@ -67,7 +67,7 @@ namespace Barotrauma.Items.Components
|
||||
private bool linkToChat = false;
|
||||
|
||||
[ConditionallyEditable(ConditionallyEditable.ConditionType.AllowLinkingWifiToChat)]
|
||||
[Serialize(false, false, description: "If enabled, any signals received from another chat-linked wifi component are displayed " +
|
||||
[Serialize(false, IsPropertySaveable.No, description: "If enabled, any signals received from another chat-linked wifi component are displayed " +
|
||||
"as chat messages in the chatbox of the player holding the item.", alwaysUseInstanceValues: true)]
|
||||
public bool LinkToChat
|
||||
{
|
||||
@@ -88,7 +88,7 @@ namespace Barotrauma.Items.Components
|
||||
#endif
|
||||
}
|
||||
|
||||
[Editable, Serialize(1.0f, true, description: "How many seconds have to pass between signals for a message to be displayed in the chatbox. " +
|
||||
[Editable, Serialize(1.0f, IsPropertySaveable.Yes, description: "How many seconds have to pass between signals for a message to be displayed in the chatbox. " +
|
||||
"Setting this to a very low value is not recommended, because it may cause an excessive amount of chat messages to be created " +
|
||||
"if there are chat-linked wifi components that transmit a continuous signal.")]
|
||||
public float MinChatMessageInterval
|
||||
@@ -97,15 +97,15 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, true, description: "If set to true, the component will only create chat messages when the received signal changes.")]
|
||||
[Editable, Serialize(false, IsPropertySaveable.Yes, description: "If set to true, the component will only create chat messages when the received signal changes.")]
|
||||
public bool DiscardDuplicateChatMessages
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public WifiComponent(Item item, XElement element)
|
||||
: base(item, element)
|
||||
public WifiComponent(Item item, ContentXElement element)
|
||||
: base (item, element)
|
||||
{
|
||||
list.Add(this);
|
||||
IsActive = true;
|
||||
@@ -214,8 +214,6 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
item.LastSentSignalRecipients.Clear();
|
||||
}
|
||||
var senderComponent = signal.source?.GetComponent<WifiComponent>();
|
||||
if (senderComponent != null && !CanReceive(senderComponent)) { return; }
|
||||
|
||||
bool chatMsgSent = false;
|
||||
|
||||
@@ -267,7 +265,7 @@ namespace Barotrauma.Items.Components
|
||||
wifiComp.item.ParentInventory.Owner != null)
|
||||
{
|
||||
string chatMsg = signal.value;
|
||||
if (senderComponent != null)
|
||||
if (sentSignalStrength <= 1.0f)
|
||||
{
|
||||
chatMsg = ChatMessage.ApplyDistanceEffect(chatMsg, 1.0f - sentSignalStrength);
|
||||
}
|
||||
|
||||
@@ -81,35 +81,35 @@ namespace Barotrauma.Items.Components
|
||||
get { return connections; }
|
||||
}
|
||||
|
||||
[Serialize(5000.0f, false, description: "The maximum distance the wire can extend (in pixels).")]
|
||||
[Serialize(5000.0f, IsPropertySaveable.No, description: "The maximum distance the wire can extend (in pixels).")]
|
||||
public float MaxLength
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "If enabled, the wire will not be visible in connection panels outside the submarine editor.")]
|
||||
[Serialize(false, IsPropertySaveable.No, description: "If enabled, the wire will not be visible in connection panels outside the submarine editor.")]
|
||||
public bool HiddenInGame
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, true, "If enabled, this wire will be ignored by the \"Lock all default wires\" setting.", alwaysUseInstanceValues: true)]
|
||||
[Editable, Serialize(false, IsPropertySaveable.Yes, "If enabled, this wire will be ignored by the \"Lock all default wires\" setting.", alwaysUseInstanceValues: true)]
|
||||
public bool NoAutoLock
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, true, "If enabled, this wire will use the sprite depth instead of a constant depth.")]
|
||||
[Editable, Serialize(false, IsPropertySaveable.Yes, "If enabled, this wire will use the sprite depth instead of a constant depth.")]
|
||||
public bool UseSpriteDepth
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Wire(Item item, XElement element)
|
||||
public Wire(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
nodes = new List<Vector2>();
|
||||
@@ -121,7 +121,7 @@ namespace Barotrauma.Items.Components
|
||||
InitProjSpecific(element);
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(XElement element);
|
||||
partial void InitProjSpecific(ContentXElement element);
|
||||
|
||||
public Connection OtherConnection(Connection connection)
|
||||
{
|
||||
@@ -455,12 +455,7 @@ namespace Barotrauma.Items.Components
|
||||
#if CLIENT
|
||||
if (GameMain.NetworkMember != null)
|
||||
{
|
||||
GameMain.Client.CreateEntityEvent(item, new object[]
|
||||
{
|
||||
NetEntityEvent.Type.ComponentState,
|
||||
item.GetComponentIndex(this),
|
||||
nodes.Count
|
||||
});
|
||||
item.CreateClientEvent(this, new ClientEventData(nodes.Count));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -482,12 +477,7 @@ namespace Barotrauma.Items.Components
|
||||
#if CLIENT
|
||||
if (GameMain.NetworkMember != null)
|
||||
{
|
||||
GameMain.Client.CreateEntityEvent(item, new object[]
|
||||
{
|
||||
NetEntityEvent.Type.ComponentState,
|
||||
item.GetComponentIndex(this),
|
||||
nodes.Count
|
||||
});
|
||||
item.CreateClientEvent(this, new ClientEventData(nodes.Count));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -785,7 +775,7 @@ namespace Barotrauma.Items.Components
|
||||
UpdateSections();
|
||||
}
|
||||
|
||||
public override void Load(XElement componentElement, bool usePrefabValues, IdRemap idRemap)
|
||||
public override void Load(ContentXElement componentElement, bool usePrefabValues, IdRemap idRemap)
|
||||
{
|
||||
base.Load(componentElement, usePrefabValues, idRemap);
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
class XorComponent : AndComponent
|
||||
{
|
||||
public XorComponent(Item item, XElement element)
|
||||
public XorComponent(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class StatusHUD : ItemComponent
|
||||
{
|
||||
public StatusHUD(Item item, XElement element)
|
||||
public StatusHUD(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class TriggerComponent : ItemComponent
|
||||
{
|
||||
[Editable, Serialize(0.0f, true, description: "The maximum amount of force applied to the triggering entitites.", alwaysUseInstanceValues: true)]
|
||||
[Editable, Serialize(0.0f, IsPropertySaveable.Yes, description: "The maximum amount of force applied to the triggering entitites.", alwaysUseInstanceValues: true)]
|
||||
public float Force { get; set; }
|
||||
|
||||
public PhysicsBody PhysicsBody { get; private set; }
|
||||
@@ -54,7 +54,7 @@ namespace Barotrauma.Items.Components
|
||||
/// </summary>
|
||||
private readonly List<Attack> attacks = new List<Attack>();
|
||||
|
||||
public TriggerComponent(Item item, XElement element) : base(item, element)
|
||||
public TriggerComponent(Item item, ContentXElement element) : base(item, element)
|
||||
{
|
||||
string triggeredByAttribute = element.GetAttributeString("triggeredby", "Character");
|
||||
if (!Enum.TryParse(triggeredByAttribute, out triggeredBy))
|
||||
@@ -72,7 +72,7 @@ namespace Barotrauma.Items.Components
|
||||
forceFluctuationInterval = Math.Max(forceFluctuationInterval, 0.01f);
|
||||
|
||||
string parentDebugName = $"TriggerComponent in {item.Name}";
|
||||
foreach (XElement subElement in element.Elements())
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
|
||||
@@ -49,8 +49,6 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private ChargingState currentChargingState;
|
||||
|
||||
private float currentBarrelSpin = 0f;
|
||||
|
||||
private readonly List<Item> activeProjectiles = new List<Item>();
|
||||
public IEnumerable<Item> ActiveProjectiles => activeProjectiles;
|
||||
|
||||
@@ -78,7 +76,7 @@ namespace Barotrauma.Items.Components
|
||||
get { return rotation; }
|
||||
}
|
||||
|
||||
[Serialize("0,0", false, description: "The position of the barrel relative to the upper left corner of the base sprite (in pixels).")]
|
||||
[Serialize("0,0", IsPropertySaveable.No, description: "The position of the barrel relative to the upper left corner of the base sprite (in pixels).")]
|
||||
public Vector2 BarrelPos
|
||||
{
|
||||
get
|
||||
@@ -92,7 +90,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize("0,0", false, description: "The projectile launching location relative to transformed barrel position (in pixels).")]
|
||||
[Serialize("0,0", IsPropertySaveable.No, description: "The projectile launching location relative to transformed barrel position (in pixels).")]
|
||||
public Vector2 FiringOffset
|
||||
{
|
||||
get;
|
||||
@@ -106,49 +104,49 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(0.0f, false, description: "The impulse applied to the physics body of the projectile (the higher the impulse, the faster the projectiles are launched).")]
|
||||
[Serialize(0.0f, IsPropertySaveable.No, description: "The impulse applied to the physics body of the projectile (the higher the impulse, the faster the projectiles are launched).")]
|
||||
public float LaunchImpulse
|
||||
{
|
||||
get { return launchImpulse; }
|
||||
set { launchImpulse = value; }
|
||||
}
|
||||
|
||||
[Editable(0.0f, 1000.0f, decimals: 3), Serialize(5.0f, false, description: "The period of time the user has to wait between shots.")]
|
||||
[Editable(0.0f, 1000.0f, decimals: 3), Serialize(5.0f, IsPropertySaveable.No, description: "The period of time the user has to wait between shots.")]
|
||||
public float Reload
|
||||
{
|
||||
get { return reloadTime; }
|
||||
set { reloadTime = value; }
|
||||
}
|
||||
|
||||
[Editable(0.1f, 10f), Serialize(1.0f, false, description: "Modifies the duration of retraction of the barrell after recoil to get back to the original position after shooting. Reload time affects this too.")]
|
||||
[Editable(0.1f, 10f), Serialize(1.0f, IsPropertySaveable.No, description: "Modifies the duration of retraction of the barrell after recoil to get back to the original position after shooting. Reload time affects this too.")]
|
||||
public float RetractionDurationMultiplier
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable(0.1f, 10f), Serialize(0.1f, false, description: "How quickly the recoil moves the barrel after launching.")]
|
||||
[Editable(0.1f, 10f), Serialize(0.1f, IsPropertySaveable.No, description: "How quickly the recoil moves the barrel after launching.")]
|
||||
public float RecoilTime
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable(0f, 1000f), Serialize(0f, false, description: "How long the barrell stays in place after the recoil and before retracting back to the original position.")]
|
||||
[Editable(0f, 1000f), Serialize(0f, IsPropertySaveable.No, description: "How long the barrell stays in place after the recoil and before retracting back to the original position.")]
|
||||
public float RetractionDelay
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(1, false, description: "How many projectiles the weapon launches when fired once.")]
|
||||
[Serialize(1, IsPropertySaveable.No, description: "How many projectiles the weapon launches when fired once.")]
|
||||
public int ProjectileCount
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "Can the turret be fired without projectiles (causing it just to execute the OnUse effects and the firing animation without actually firing anything).")]
|
||||
[Serialize(false, IsPropertySaveable.No, description: "Can the turret be fired without projectiles (causing it just to execute the OnUse effects and the firing animation without actually firing anything).")]
|
||||
public bool LaunchWithoutProjectile
|
||||
{
|
||||
get;
|
||||
@@ -156,7 +154,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
[Editable(VectorComponentLabels = new string[] { "editable.minvalue", "editable.maxvalue" }),
|
||||
Serialize("0.0,0.0", true, description: "The range at which the barrel can rotate.", alwaysUseInstanceValues: true)]
|
||||
Serialize("0.0,0.0", IsPropertySaveable.Yes, description: "The range at which the barrel can rotate.", alwaysUseInstanceValues: true)]
|
||||
public Vector2 RotationLimits
|
||||
{
|
||||
get
|
||||
@@ -179,7 +177,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(0.0f, false, description: "Random spread applied to the firing angle of the projectiles (in degrees).")]
|
||||
[Serialize(0.0f, IsPropertySaveable.No, description: "Random spread applied to the firing angle of the projectiles (in degrees).")]
|
||||
public float Spread
|
||||
{
|
||||
get;
|
||||
@@ -187,7 +185,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
[Editable(0.0f, 1000.0f, DecimalCount = 2),
|
||||
Serialize(5.0f, false, description: "How much torque is applied to rotate the barrel when the item is used by a character"
|
||||
Serialize(5.0f, IsPropertySaveable.No, description: "How much torque is applied to rotate the barrel when the item is used by a character"
|
||||
+ " with insufficient skills to operate it. Higher values make the barrel rotate faster.")]
|
||||
public float SpringStiffnessLowSkill
|
||||
{
|
||||
@@ -195,7 +193,7 @@ namespace Barotrauma.Items.Components
|
||||
private set;
|
||||
}
|
||||
[Editable(0.0f, 1000.0f, DecimalCount = 2),
|
||||
Serialize(2.0f, false, description: "How much torque is applied to rotate the barrel when the item is used by a character"
|
||||
Serialize(2.0f, IsPropertySaveable.No, description: "How much torque is applied to rotate the barrel when the item is used by a character"
|
||||
+ " with sufficient skills to operate it. Higher values make the barrel rotate faster.")]
|
||||
public float SpringStiffnessHighSkill
|
||||
{
|
||||
@@ -204,7 +202,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
[Editable(0.0f, 1000.0f, DecimalCount = 2),
|
||||
Serialize(50.0f, false, description: "How much torque is applied to resist the movement of the barrel when the item is used by a character"
|
||||
Serialize(50.0f, IsPropertySaveable.No, description: "How much torque is applied to resist the movement of the barrel when the item is used by a character"
|
||||
+ " with insufficient skills to operate it. Higher values make the aiming more \"snappy\", stopping the barrel from swinging around the direction it's being aimed at.")]
|
||||
public float SpringDampingLowSkill
|
||||
{
|
||||
@@ -212,7 +210,7 @@ namespace Barotrauma.Items.Components
|
||||
private set;
|
||||
}
|
||||
[Editable(0.0f, 1000.0f, DecimalCount = 2),
|
||||
Serialize(10.0f, false, description: "How much torque is applied to resist the movement of the barrel when the item is used by a character"
|
||||
Serialize(10.0f, IsPropertySaveable.No, description: "How much torque is applied to resist the movement of the barrel when the item is used by a character"
|
||||
+ " with sufficient skills to operate it. Higher values make the aiming more \"snappy\", stopping the barrel from swinging around the direction it's being aimed at.")]
|
||||
public float SpringDampingHighSkill
|
||||
{
|
||||
@@ -221,28 +219,28 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
[Editable(0.0f, 100.0f, DecimalCount = 2),
|
||||
Serialize(1.0f, false, description: "Maximum angular velocity of the barrel when used by a character with insufficient skills to operate it.")]
|
||||
Serialize(1.0f, IsPropertySaveable.No, description: "Maximum angular velocity of the barrel when used by a character with insufficient skills to operate it.")]
|
||||
public float RotationSpeedLowSkill
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
[Editable(0.0f, 100.0f, DecimalCount = 2),
|
||||
Serialize(5.0f, false, description: "Maximum angular velocity of the barrel when used by a character with sufficient skills to operate it."),]
|
||||
Serialize(5.0f, IsPropertySaveable.No, description: "Maximum angular velocity of the barrel when used by a character with sufficient skills to operate it."),]
|
||||
public float RotationSpeedHighSkill
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(1.0f, false, description: "How fast the turret can rotate while firing (for charged weapons).")]
|
||||
[Serialize(1.0f, IsPropertySaveable.No, description: "How fast the turret can rotate while firing (for charged weapons).")]
|
||||
public float FiringRotationSpeedModifier
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(false, true, description: "Whether the turret should always charge-up fully to shoot.")]
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Whether the turret should always charge-up fully to shoot.")]
|
||||
public bool SingleChargedShot
|
||||
{
|
||||
get;
|
||||
@@ -251,7 +249,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private float prevScale;
|
||||
float prevBaseRotation;
|
||||
[Serialize(0.0f, true, description: "The angle of the turret's base in degrees.", alwaysUseInstanceValues: true)]
|
||||
[Serialize(0.0f, IsPropertySaveable.Yes, description: "The angle of the turret's base in degrees.", alwaysUseInstanceValues: true)]
|
||||
public float BaseRotation
|
||||
{
|
||||
get { return item.Rotation; }
|
||||
@@ -262,33 +260,33 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(3000.0f, true, description: "How close to a target the turret has to be for an AI character to fire it.")]
|
||||
[Serialize(3000.0f, IsPropertySaveable.Yes, description: "How close to a target the turret has to be for an AI character to fire it.")]
|
||||
public float AIRange
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(-1, true, description: "The turret won't fire additional projectiles if the number of previously fired, still active projectiles reaches this limit. If set to -1, there is no limit to the number of projectiles.")]
|
||||
[Serialize(-1, IsPropertySaveable.Yes, description: "The turret won't fire additional projectiles if the number of previously fired, still active projectiles reaches this limit. If set to -1, there is no limit to the number of projectiles.")]
|
||||
public int MaxActiveProjectiles
|
||||
{
|
||||
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;
|
||||
private set;
|
||||
}
|
||||
|
||||
public Turret(Item item, XElement element)
|
||||
public Turret(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
@@ -315,7 +313,7 @@ namespace Barotrauma.Items.Components
|
||||
InitProjSpecific(element);
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(XElement element);
|
||||
partial void InitProjSpecific(ContentXElement element);
|
||||
|
||||
private void UpdateTransformedBarrelPos()
|
||||
{
|
||||
@@ -330,10 +328,10 @@ namespace Barotrauma.Items.Components
|
||||
public override void OnMapLoaded()
|
||||
{
|
||||
base.OnMapLoaded();
|
||||
FindLightComponent();
|
||||
if (loadedRotationLimits.HasValue) { RotationLimits = loadedRotationLimits.Value; }
|
||||
if (loadedBaseRotation.HasValue) { BaseRotation = loadedBaseRotation.Value; }
|
||||
targetRotation = rotation;
|
||||
FindLightComponent();
|
||||
UpdateTransformedBarrelPos();
|
||||
}
|
||||
|
||||
@@ -456,7 +454,7 @@ namespace Barotrauma.Items.Components
|
||||
// Do not increase the weapons skill when operating a turret in an outpost level
|
||||
if (user?.Info != null && (GameMain.GameSession?.Campaign == null || !Level.IsLoadedOutpost))
|
||||
{
|
||||
user.Info.IncreaseSkillLevel("weapons",
|
||||
user.Info.IncreaseSkillLevel("weapons".ToIdentifier(),
|
||||
SkillSettings.Current.SkillIncreasePerSecondWhenOperatingTurret * deltaTime / Math.Max(user.GetSkillLevel("weapons"), 1.0f));
|
||||
}
|
||||
|
||||
@@ -601,7 +599,7 @@ namespace Barotrauma.Items.Components
|
||||
//use linked projectile containers in case they have to react to the turret being launched somehow
|
||||
//(play a sound, spawn more projectiles)
|
||||
if (!(e is Item linkedItem)) { continue; }
|
||||
if (!item.prefab.IsLinkAllowed(e.prefab)) { continue; }
|
||||
if (!item.Prefab.IsLinkAllowed(e.Prefab)) { continue; }
|
||||
if (linkedItem.Condition <= 0.0f)
|
||||
{
|
||||
loaderBroken = true;
|
||||
@@ -644,7 +642,7 @@ namespace Barotrauma.Items.Components
|
||||
foreach (MapEntity e in item.linkedTo)
|
||||
{
|
||||
if (!(e is Item linkedItem)) { continue; }
|
||||
if (!item.prefab.IsLinkAllowed(e.prefab)) { continue; }
|
||||
if (!((MapEntity)item).Prefab.IsLinkAllowed(e.Prefab)) { continue; }
|
||||
if (linkedItem.GetComponent<Repairable>() is Repairable repairable && repairable.IsTinkering && linkedItem.HasTag("turretammosource"))
|
||||
{
|
||||
tinkeringStrength = repairable.TinkeringStrength;
|
||||
@@ -653,8 +651,9 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (!ignorePower)
|
||||
{
|
||||
var batteries = item.GetConnectedComponents<PowerContainer>();
|
||||
List<PowerContainer> batteries = GetConnectedBatteries();
|
||||
float neededPower = GetPowerRequiredToShoot();
|
||||
|
||||
// tinkering is currently not factored into the common method as it is checked only when shooting
|
||||
// but this is a minor issue that causes mostly cosmetic woes. might still be worth refactoring later
|
||||
neededPower /= 1f + (tinkeringStrength * TinkeringPowerCostReduction);
|
||||
@@ -735,6 +734,16 @@ namespace Barotrauma.Items.Components
|
||||
return true;
|
||||
}
|
||||
|
||||
private readonly struct EventData : IEventData
|
||||
{
|
||||
public readonly Item Projectile;
|
||||
|
||||
public EventData(Item projectile)
|
||||
{
|
||||
Projectile = projectile;
|
||||
}
|
||||
}
|
||||
|
||||
private void Launch(Item projectile, Character user = null, float? launchRotation = null, float tinkeringStrength = 0f)
|
||||
{
|
||||
reload = reloadTime;
|
||||
@@ -748,7 +757,7 @@ namespace Barotrauma.Items.Components
|
||||
if (projectile != null)
|
||||
{
|
||||
activeProjectiles.Add(projectile);
|
||||
projectile.Drop(null);
|
||||
projectile.Drop(null, setTransform: false);
|
||||
if (projectile.body != null)
|
||||
{
|
||||
projectile.body.Dir = 1.0f;
|
||||
@@ -786,12 +795,11 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
if (projectile.Container != null) { projectile.Container.RemoveContained(projectile); }
|
||||
}
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
GameMain.NetworkMember.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ComponentState, item.GetComponentIndex(this), projectile });
|
||||
projectile.Container?.RemoveContained(projectile);
|
||||
}
|
||||
#if SERVER
|
||||
item.CreateServerEvent(this, new EventData(projectile));
|
||||
#endif
|
||||
|
||||
ApplyStatusEffects(ActionType.OnUse, 1.0f, user: user);
|
||||
LaunchProjSpecific();
|
||||
@@ -864,8 +872,8 @@ namespace Barotrauma.Items.Components
|
||||
foreach (var character in Character.CharacterList)
|
||||
{
|
||||
if (character == null || character.Removed || character.IsDead) { continue; }
|
||||
if (character.Params.Group.Equals(ai.Config.Entity, StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
bool isHuman = character.IsHuman || character.Params.Group.Equals(CharacterPrefab.HumanSpeciesName, StringComparison.OrdinalIgnoreCase);
|
||||
if (character.Params.Group == ai.Config.Entity) { continue; }
|
||||
bool isHuman = character.IsHuman || character.Params.Group == CharacterPrefab.HumanSpeciesName;
|
||||
if (isHuman)
|
||||
{
|
||||
if (!targetHumans)
|
||||
@@ -901,7 +909,7 @@ namespace Barotrauma.Items.Components
|
||||
closestDist = shootDistance * shootDistance;
|
||||
if (closestSub != null)
|
||||
{
|
||||
foreach (var hull in Hull.hullList)
|
||||
foreach (var hull in Hull.HullList)
|
||||
{
|
||||
if (!closestSub.IsEntityFoundOnThisSub(hull, true)) { continue; }
|
||||
float dist = Vector2.DistanceSquared(hull.WorldPosition, item.WorldPosition);
|
||||
@@ -984,8 +992,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (character.AIController.SelectedAiTarget?.Entity is Character previousTarget && previousTarget.IsDead)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogTurretTargetDead"),
|
||||
identifier: "killedtarget" + previousTarget.ID,
|
||||
character.Speak(TextManager.Get("DialogTurretTargetDead").Value,
|
||||
identifier: $"killedtarget{previousTarget.ID}".ToIdentifier(),
|
||||
minDurationBetweenSimilar: 10.0f);
|
||||
character.AIController.SelectTarget(null);
|
||||
}
|
||||
@@ -993,7 +1001,7 @@ namespace Barotrauma.Items.Components
|
||||
bool canShoot = true;
|
||||
if (!HasPowerToShoot())
|
||||
{
|
||||
var batteries = item.GetConnectedComponents<PowerContainer>();
|
||||
List<PowerContainer> batteries = GetConnectedBatteries();
|
||||
float lowestCharge = 0.0f;
|
||||
PowerContainer batteryToLoad = null;
|
||||
foreach (PowerContainer battery in batteries)
|
||||
@@ -1013,8 +1021,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogSupercapacitorIsBroken"),
|
||||
identifier: "supercapacitorisbroken",
|
||||
character.Speak(TextManager.Get("DialogSupercapacitorIsBroken").Value,
|
||||
identifier: "supercapacitorisbroken".ToIdentifier(),
|
||||
minDurationBetweenSimilar: 30.0f);
|
||||
canShoot = false;
|
||||
}
|
||||
@@ -1023,13 +1031,13 @@ namespace Barotrauma.Items.Components
|
||||
if (batteryToLoad == null) { return true; }
|
||||
if (batteryToLoad.RechargeSpeed < batteryToLoad.MaxRechargeSpeed * 0.4f)
|
||||
{
|
||||
objective.AddSubObjective(new AIObjectiveOperateItem(batteryToLoad, character, objective.objectiveManager, option: "", requireEquip: false));
|
||||
objective.AddSubObjective(new AIObjectiveOperateItem(batteryToLoad, character, objective.objectiveManager, option: Identifier.Empty, requireEquip: false));
|
||||
return false;
|
||||
}
|
||||
if (lowestCharge <= 0 && batteryToLoad.Item.ConditionPercentage > 0)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogTurretHasNoPower"),
|
||||
identifier: "turrethasnopower",
|
||||
character.Speak(TextManager.Get("DialogTurretHasNoPower").Value,
|
||||
identifier: "turrethasnopower".ToIdentifier(),
|
||||
minDurationBetweenSimilar: 30.0f);
|
||||
canShoot = false;
|
||||
}
|
||||
@@ -1040,7 +1048,7 @@ namespace Barotrauma.Items.Components
|
||||
foreach (MapEntity e in item.linkedTo)
|
||||
{
|
||||
if (!item.IsInteractable(character)) { continue; }
|
||||
if (!item.prefab.IsLinkAllowed(e.prefab)) { continue; }
|
||||
if (!((MapEntity)item).Prefab.IsLinkAllowed(e.Prefab)) { continue; }
|
||||
if (e is Item projectileContainer)
|
||||
{
|
||||
var container = projectileContainer.GetComponent<ItemContainer>();
|
||||
@@ -1070,8 +1078,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (character.IsOnPlayerTeam)
|
||||
{
|
||||
character.Speak(TextManager.GetWithVariable("DialogCannotLoadTurret", "[itemname]", item.Name, formatCapitals: true),
|
||||
identifier: "cannotloadturret",
|
||||
character.Speak(TextManager.GetWithVariable("DialogCannotLoadTurret", "[itemname]", item.Name, formatCapitals: FormatCapitals.Yes).Value,
|
||||
identifier: "cannotloadturret".ToIdentifier(),
|
||||
minDurationBetweenSimilar: 30.0f);
|
||||
}
|
||||
return true;
|
||||
@@ -1079,11 +1087,11 @@ namespace Barotrauma.Items.Components
|
||||
if (objective.SubObjectives.None())
|
||||
{
|
||||
var loadItemsObjective = AIContainItems<Turret>(container, character, objective, usableProjectileCount + 1, equip: true, removeEmpty: true, dropItemOnDeselected: true);
|
||||
loadItemsObjective.ignoredContainerIdentifiers = new string[] { containerItem.prefab.Identifier };
|
||||
loadItemsObjective.ignoredContainerIdentifiers = new Identifier[] { ((MapEntity)containerItem).Prefab.Identifier };
|
||||
if (character.IsOnPlayerTeam)
|
||||
{
|
||||
character.Speak(TextManager.GetWithVariable("DialogLoadTurret", "[itemname]", item.Name, formatCapitals: true),
|
||||
identifier: "loadturret",
|
||||
character.Speak(TextManager.GetWithVariable("DialogLoadTurret", "[itemname]", item.Name, formatCapitals: FormatCapitals.Yes).Value,
|
||||
identifier: "loadturret".ToIdentifier(),
|
||||
minDurationBetweenSimilar: 30.0f);
|
||||
}
|
||||
loadItemsObjective.Abandoned += CheckRemainingAmmo;
|
||||
@@ -1094,18 +1102,18 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (!character.IsOnPlayerTeam) { return; }
|
||||
if (character.Submarine != Submarine.MainSub) { return; }
|
||||
string ammoType = container.ContainableItemIdentifiers.FirstOrDefault() ?? "ammobox";
|
||||
Identifier ammoType = container.ContainableItemIdentifiers.FirstOrNull() ?? "ammobox".ToIdentifier();
|
||||
int remainingAmmo = Submarine.MainSub.GetItems(false).Count(i => i.HasTag(ammoType) && i.Condition > 1);
|
||||
if (remainingAmmo == 0)
|
||||
{
|
||||
character.Speak(TextManager.Get($"DialogOutOf{ammoType}", fallBackTag: "DialogOutOfTurretAmmo"),
|
||||
identifier: "outofammo",
|
||||
character.Speak(TextManager.Get($"DialogOutOf{ammoType}", "DialogOutOfTurretAmmo").Value,
|
||||
identifier: "outofammo".ToIdentifier(),
|
||||
minDurationBetweenSimilar: 30.0f);
|
||||
}
|
||||
else if (remainingAmmo < 3)
|
||||
{
|
||||
character.Speak(TextManager.Get($"DialogLowOn{ammoType}"),
|
||||
identifier: "outofammo",
|
||||
character.Speak(TextManager.Get($"DialogLowOn{ammoType}").Value,
|
||||
identifier: "outofammo".ToIdentifier(),
|
||||
minDurationBetweenSimilar: 30.0f);
|
||||
}
|
||||
}
|
||||
@@ -1264,29 +1272,29 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (character.AIController.SelectedAiTarget == null && !hadCurrentTarget)
|
||||
{
|
||||
if (GameMain.Config.RecentlyEncounteredCreatures.Contains(closestEnemy.SpeciesName))
|
||||
if (CreatureMetrics.Instance.RecentlyEncountered.Contains(closestEnemy.SpeciesName))
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogNewTargetSpotted"),
|
||||
identifier: "newtargetspotted",
|
||||
character.Speak(TextManager.Get("DialogNewTargetSpotted").Value,
|
||||
identifier: "newtargetspotted".ToIdentifier(),
|
||||
minDurationBetweenSimilar: 30.0f);
|
||||
}
|
||||
else if (GameMain.Config.EncounteredCreatures.Any(name => name.Equals(closestEnemy.SpeciesName, StringComparison.OrdinalIgnoreCase)))
|
||||
else if (CreatureMetrics.Instance.Encountered.Contains(closestEnemy.SpeciesName))
|
||||
{
|
||||
character.Speak(TextManager.GetWithVariable("DialogIdentifiedTargetSpotted", "[speciesname]", closestEnemy.DisplayName),
|
||||
identifier: "identifiedtargetspotted",
|
||||
character.Speak(TextManager.GetWithVariable("DialogIdentifiedTargetSpotted", "[speciesname]", closestEnemy.DisplayName).Value,
|
||||
identifier: "identifiedtargetspotted".ToIdentifier(),
|
||||
minDurationBetweenSimilar: 30.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogUnidentifiedTargetSpotted"),
|
||||
identifier: "unidentifiedtargetspotted",
|
||||
character.Speak(TextManager.Get("DialogUnidentifiedTargetSpotted").Value,
|
||||
identifier: "unidentifiedtargetspotted".ToIdentifier(),
|
||||
minDurationBetweenSimilar: 5.0f);
|
||||
}
|
||||
}
|
||||
else if (GameMain.Config.EncounteredCreatures.None(name => name.Equals(closestEnemy.SpeciesName, StringComparison.OrdinalIgnoreCase)))
|
||||
else if (!CreatureMetrics.Instance.Encountered.Contains(closestEnemy.SpeciesName))
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogUnidentifiedTargetSpotted"),
|
||||
identifier: "unidentifiedtargetspotted",
|
||||
character.Speak(TextManager.Get("DialogUnidentifiedTargetSpotted").Value,
|
||||
identifier: "unidentifiedtargetspotted".ToIdentifier(),
|
||||
minDurationBetweenSimilar: 5.0f);
|
||||
}
|
||||
character.AddEncounter(closestEnemy);
|
||||
@@ -1295,8 +1303,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else if (closestEnemy == null && character.IsOnPlayerTeam)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogIceSpireSpotted"),
|
||||
identifier: "icespirespotted",
|
||||
character.Speak(TextManager.Get("DialogIceSpireSpotted").Value,
|
||||
identifier: "icespirespotted".ToIdentifier(),
|
||||
minDurationBetweenSimilar: 60.0f);
|
||||
}
|
||||
|
||||
@@ -1339,8 +1347,8 @@ namespace Barotrauma.Items.Components
|
||||
if (!shoot) { return false; }
|
||||
if (character.IsOnPlayerTeam)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogFireTurret"),
|
||||
identifier: "fireturret",
|
||||
character.Speak(TextManager.Get("DialogFireTurret").Value,
|
||||
identifier: "fireturret".ToIdentifier(),
|
||||
minDurationBetweenSimilar: 30.0f);
|
||||
}
|
||||
character.SetInput(InputType.Shoot, true, true);
|
||||
@@ -1349,6 +1357,14 @@ namespace Barotrauma.Items.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Turret doesn't consume grid power, directly takes from the batteries on its grid instead.
|
||||
/// </summary>
|
||||
public override float GetCurrentPowerConsumption(Connection conn = null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private bool CanShoot(Body targetBody, Character user = null, WreckAI ai = null, bool targetSubmarines = true)
|
||||
{
|
||||
if (targetBody == null) { return false; }
|
||||
@@ -1372,7 +1388,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
if (ai != null)
|
||||
{
|
||||
if (targetCharacter.Params.Group.Equals(ai.Config.Entity, StringComparison.OrdinalIgnoreCase))
|
||||
if (targetCharacter.Params.Group == ai.Config.Entity)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -1458,7 +1474,7 @@ namespace Barotrauma.Items.Components
|
||||
for (int j = 0; j < item.linkedTo.Count; j++)
|
||||
{
|
||||
var e = item.linkedTo[(j + currentLoaderIndex) % item.linkedTo.Count];
|
||||
if (!item.prefab.IsLinkAllowed(e.prefab)) { continue; }
|
||||
if (!item.Prefab.IsLinkAllowed(e.Prefab)) { continue; }
|
||||
if (e is Item projectileContainer)
|
||||
{
|
||||
CheckProjectileContainer(projectileContainer, projectiles, out bool stopSearching);
|
||||
@@ -1602,7 +1618,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private Vector2? loadedRotationLimits;
|
||||
private float? loadedBaseRotation;
|
||||
public override void Load(XElement componentElement, bool usePrefabValues, IdRemap idRemap)
|
||||
public override void Load(ContentXElement componentElement, bool usePrefabValues, IdRemap idRemap)
|
||||
{
|
||||
base.Load(componentElement, usePrefabValues, idRemap);
|
||||
loadedRotationLimits = componentElement.GetAttributeVector2("rotationlimits", RotationLimits);
|
||||
@@ -1621,11 +1637,11 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
|
||||
public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
if (extraData.Length > 2)
|
||||
if (TryExtractEventData(extraData, out EventData eventData))
|
||||
{
|
||||
msg.Write(!(extraData[2] is Item item) ? ushort.MaxValue : item.ID);
|
||||
msg.Write(eventData.Projectile.ID);
|
||||
msg.WriteRangedSingle(MathHelper.Clamp(rotation, minRotation, maxRotation), minRotation, maxRotation, 16);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Networking;
|
||||
using System.Collections.Immutable;
|
||||
using Barotrauma.Abilities;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -24,16 +25,16 @@ namespace Barotrauma
|
||||
|
||||
class WearableSprite
|
||||
{
|
||||
public string UnassignedSpritePath { get; private set; }
|
||||
public ContentPath UnassignedSpritePath { get; private set; }
|
||||
public string SpritePath { get; private set; }
|
||||
public XElement SourceElement { get; private set; }
|
||||
public ContentXElement SourceElement { get; private set; }
|
||||
|
||||
public WearableType Type { get; private set; }
|
||||
private Sprite _sprite;
|
||||
public Sprite Sprite
|
||||
{
|
||||
get { return _sprite; }
|
||||
set
|
||||
private set
|
||||
{
|
||||
if (value == _sprite) { return; }
|
||||
if (_sprite != null)
|
||||
@@ -85,29 +86,29 @@ namespace Barotrauma
|
||||
|
||||
public int Variant { get; set; }
|
||||
|
||||
private Gender _gender;
|
||||
private Character _picker;
|
||||
/// <summary>
|
||||
/// None = Any/Not Defined -> no effect.
|
||||
/// Changing the gender forces re-initialization, because the textures can be different for male and female characters.
|
||||
/// </summary>
|
||||
public Gender Gender
|
||||
public Character Picker
|
||||
{
|
||||
get { return _gender; }
|
||||
get { return _picker; }
|
||||
set
|
||||
{
|
||||
if (value == _gender) { return; }
|
||||
_gender = value;
|
||||
if (value == _picker) { return; }
|
||||
_picker = value;
|
||||
IsInitialized = false;
|
||||
UnassignedSpritePath = ParseSpritePath(SourceElement.GetAttributeString("texture", string.Empty));
|
||||
Init(_gender);
|
||||
UnassignedSpritePath = ParseSpritePath(SourceElement);
|
||||
Init(_picker);
|
||||
}
|
||||
}
|
||||
|
||||
public WearableSprite(XElement subElement, WearableType type)
|
||||
public WearableSprite(ContentXElement subElement, WearableType type)
|
||||
{
|
||||
Type = type;
|
||||
SourceElement = subElement;
|
||||
UnassignedSpritePath = subElement.GetAttributeString("texture", string.Empty);
|
||||
UnassignedSpritePath = subElement.GetAttributeContentPath("texture") ?? ContentPath.Empty;
|
||||
Init();
|
||||
switch (type)
|
||||
{
|
||||
@@ -131,34 +132,48 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// Note: this constructor cannot initialize automatically, because the gender is unknown at this point. We only know it when the item is equipped.
|
||||
/// </summary>
|
||||
public WearableSprite(XElement subElement, Wearable wearable, int variant = 0)
|
||||
public WearableSprite(ContentXElement subElement, Wearable wearable, int variant = 0)
|
||||
{
|
||||
Type = WearableType.Item;
|
||||
WearableComponent = wearable;
|
||||
Variant = Math.Max(variant, 0);
|
||||
UnassignedSpritePath = ParseSpritePath(subElement.GetAttributeString("texture", string.Empty));
|
||||
UnassignedSpritePath = ParseSpritePath(subElement);
|
||||
SourceElement = subElement;
|
||||
}
|
||||
|
||||
private string ParseSpritePath(string texturePath) => texturePath.Contains("/") ? texturePath : $"{Path.GetDirectoryName(WearableComponent.Item.Prefab.FilePath)}/{texturePath}";
|
||||
private ContentPath ParseSpritePath(ContentXElement element)
|
||||
{
|
||||
if (element.DoesAttributeReferenceFileNameAlone("texture"))
|
||||
{
|
||||
string textureName = element.GetAttributeString("texture", "");
|
||||
return ContentPath.FromRaw(
|
||||
element.ContentPackage,
|
||||
$"{Path.GetDirectoryName(WearableComponent.Item.Prefab.FilePath)}/{textureName}");
|
||||
}
|
||||
else
|
||||
{
|
||||
return element.GetAttributeContentPath("texture") ?? ContentPath.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public void ParsePath(bool parseSpritePath)
|
||||
{
|
||||
string tempPath = UnassignedSpritePath;
|
||||
if (_gender != Gender.None)
|
||||
SpritePath = UnassignedSpritePath.Value;
|
||||
if (_picker?.Info != null)
|
||||
{
|
||||
tempPath = tempPath.Replace("[GENDER]", (_gender == Gender.Female) ? "female" : "male");
|
||||
SpritePath = _picker.Info.ReplaceVars(SpritePath);
|
||||
}
|
||||
SpritePath = tempPath.Replace("[VARIANT]", Variant.ToString());
|
||||
SpritePath = SpritePath.Replace("[VARIANT]", Variant.ToString());
|
||||
if (!File.Exists(SpritePath))
|
||||
{
|
||||
// If the variant does not exist, parse the path so that it uses first variant.
|
||||
SpritePath = tempPath.Replace("[VARIANT]", "1");
|
||||
SpritePath = SpritePath.Replace("[VARIANT]", "1");
|
||||
}
|
||||
if (!File.Exists(SpritePath) && _gender == Gender.None)
|
||||
if (!File.Exists(SpritePath) && _picker?.Info == null)
|
||||
{
|
||||
// If there's no sprite for Gender.None does not exist, try to use male sprite
|
||||
SpritePath = tempPath.Replace("[GENDER]", "male");
|
||||
// If there's no character info is defined, try to use first tagset from CharacterInfoPrefab
|
||||
var charInfoPrefab = CharacterPrefab.HumanPrefab.CharacterInfoPrefab;
|
||||
SpritePath = charInfoPrefab.ReplaceVars(SpritePath, charInfoPrefab.Heads.First());
|
||||
}
|
||||
if (parseSpritePath)
|
||||
{
|
||||
@@ -167,10 +182,11 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
public bool IsInitialized { get; private set; }
|
||||
public void Init(Gender gender = Gender.None)
|
||||
public void Init(Character picker = null)
|
||||
{
|
||||
if (IsInitialized) { return; }
|
||||
_gender = UnassignedSpritePath.Contains("[GENDER]") ? gender : Gender.None;
|
||||
|
||||
_picker = picker;
|
||||
ParsePath(false);
|
||||
Sprite?.Remove();
|
||||
Sprite = new Sprite(SourceElement, file: SpritePath);
|
||||
@@ -226,13 +242,13 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class Wearable : Pickable, IServerSerializable
|
||||
{
|
||||
private readonly XElement[] wearableElements;
|
||||
private readonly ContentXElement[] wearableElements;
|
||||
private readonly WearableSprite[] wearableSprites;
|
||||
private readonly LimbType[] limbType;
|
||||
private readonly Limb[] limb;
|
||||
|
||||
private readonly List<DamageModifier> damageModifiers;
|
||||
public readonly Dictionary<string, float> SkillModifiers = new Dictionary<string, float>();
|
||||
public readonly Dictionary<Identifier, float> SkillModifiers;
|
||||
|
||||
public readonly Dictionary<StatTypes, float> WearableStatValues = new Dictionary<StatTypes, float>();
|
||||
|
||||
@@ -285,28 +301,29 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public Wearable(Item item, XElement element) : base(item, element)
|
||||
public Wearable(Item item, ContentXElement element) : base(item, element)
|
||||
{
|
||||
this.item = item;
|
||||
|
||||
damageModifiers = new List<DamageModifier>();
|
||||
SkillModifiers = new Dictionary<Identifier, float>();
|
||||
|
||||
int spriteCount = element.Elements().Count(x => x.Name.ToString() == "sprite");
|
||||
Variants = element.GetAttributeInt("variants", 0);
|
||||
variant = Rand.Range(1, Variants + 1, Rand.RandSync.Server);
|
||||
variant = Rand.Range(1, Variants + 1, Rand.RandSync.ServerAndClient);
|
||||
wearableSprites = new WearableSprite[spriteCount];
|
||||
wearableElements = new XElement[spriteCount];
|
||||
wearableElements = new ContentXElement[spriteCount];
|
||||
limbType = new LimbType[spriteCount];
|
||||
limb = new Limb[spriteCount];
|
||||
AutoEquipWhenFull = element.GetAttributeBool("autoequipwhenfull", true);
|
||||
DisplayContainedStatus = element.GetAttributeBool("displaycontainedstatus", false);
|
||||
int i = 0;
|
||||
foreach (XElement subElement in element.Elements())
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "sprite":
|
||||
if (subElement.Attribute("texture") == null)
|
||||
if (subElement.GetAttribute("texture") == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Item \"" + item.Name + "\" doesn't have a texture specified!");
|
||||
return;
|
||||
@@ -318,7 +335,7 @@ namespace Barotrauma.Items.Components
|
||||
wearableSprites[i] = new WearableSprite(subElement, this, variant);
|
||||
wearableElements[i] = subElement;
|
||||
|
||||
foreach (XElement lightElement in subElement.Elements())
|
||||
foreach (var lightElement in subElement.Elements())
|
||||
{
|
||||
if (!lightElement.Name.ToString().Equals("lightcomponent", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
wearableSprites[i].LightComponent = new LightComponent(item, lightElement)
|
||||
@@ -334,7 +351,7 @@ namespace Barotrauma.Items.Components
|
||||
damageModifiers.Add(new DamageModifier(subElement, item.Name + ", Wearable"));
|
||||
break;
|
||||
case "skillmodifier":
|
||||
string skillIdentifier = subElement.GetAttributeString("skillidentifier", string.Empty);
|
||||
Identifier skillIdentifier = subElement.GetAttributeIdentifier("skillidentifier", Identifier.Empty);
|
||||
float skillValue = subElement.GetAttributeFloat("skillvalue", 0f);
|
||||
if (SkillModifiers.ContainsKey(skillIdentifier))
|
||||
{
|
||||
@@ -382,12 +399,9 @@ namespace Barotrauma.Items.Components
|
||||
for (int i = 0; i < wearableSprites.Length; i++ )
|
||||
{
|
||||
var wearableSprite = wearableSprites[i];
|
||||
if (!wearableSprite.IsInitialized) { wearableSprite.Init(picker.Info?.Gender ?? Gender.None); }
|
||||
if (picker.Info != null && picker.Info?.Gender != Gender.None && (wearableSprite.Gender != Gender.None))
|
||||
{
|
||||
// If the item is gender specific (it has a different textures for male and female), we have to change the gender here so that the texture is updated.
|
||||
wearableSprite.Gender = picker.Info.Gender;
|
||||
}
|
||||
if (!wearableSprite.IsInitialized) { wearableSprite.Init(picker); }
|
||||
// If the item is gender specific (it has a different textures for male and female), we have to change the gender here so that the texture is updated.
|
||||
wearableSprite.Picker = picker;
|
||||
|
||||
Limb equipLimb = character.AnimController.GetLimb(limbType[i]);
|
||||
if (equipLimb == null) { continue; }
|
||||
@@ -512,7 +526,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
private int loadedVariant = -1;
|
||||
public override void Load(XElement componentElement, bool usePrefabValues, IdRemap idRemap)
|
||||
public override void Load(ContentXElement componentElement, bool usePrefabValues, IdRemap idRemap)
|
||||
{
|
||||
base.Load(componentElement, usePrefabValues, idRemap);
|
||||
loadedVariant = componentElement.GetAttributeInt("variant", -1);
|
||||
@@ -526,16 +540,16 @@ namespace Barotrauma.Items.Components
|
||||
Variant = loadedVariant;
|
||||
}
|
||||
}
|
||||
public override void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
|
||||
public override void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
msg.Write((byte)Variant);
|
||||
base.ServerWrite(msg, c, extraData);
|
||||
base.ServerEventWrite(msg, c, extraData);
|
||||
}
|
||||
|
||||
public override void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public override void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
Variant = (int)msg.ReadByte();
|
||||
base.ClientRead(type, msg, sendingTime);
|
||||
base.ClientEventRead(msg, sendingTime);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user