Release 1.11.4.1 (Winter Update)

This commit is contained in:
Markus Isberg
2025-12-08 14:56:47 +00:00
parent 21e34e5cd8
commit 598966f200
121 changed files with 1614 additions and 819 deletions
@@ -226,6 +226,14 @@ namespace Barotrauma
return false;
}
public bool IsSlotEmpty(InvSlotType limbSlot)
{
for (int i = 0; i < slots.Length; i++)
{
if (SlotTypes[i] == limbSlot && slots[i].Empty()) { return true; }
}
return false;
}
/// <summary>
/// Can the item be put in the inventory in a slot of the specified type (i.e. is there a suitable free slot or a stack the item can be put in).
@@ -438,7 +446,8 @@ namespace Barotrauma
}
int placedInSlot = -1;
foreach (InvSlotType allowedSlot in allowedSlots)
//order by whether the slot is empty, i.e. try putting in free slots first before trying to unequip items from occupied slots
foreach (InvSlotType allowedSlot in allowedSlots.OrderBy(slotType => IsSlotEmpty(slotType) ? 0 : 1))
{
if (allowedSlot.HasFlag(InvSlotType.RightHand) && character.AnimController.GetLimb(LimbType.RightHand) == null) { continue; }
if (allowedSlot.HasFlag(InvSlotType.LeftHand) && character.AnimController.GetLimb(LimbType.LeftHand) == null) { continue; }
@@ -87,6 +87,13 @@ namespace Barotrauma.Items.Components
"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; }
[Serialize(false, IsPropertySaveable.Yes, description: "Was the docking port docked at the end of the previous round.")]
public bool WasDocked
{
get;
set;
}
public DockingPort DockingTarget { get; private set; }
/// <summary>
@@ -280,6 +287,9 @@ namespace Barotrauma.Items.Components
OnDocked?.Invoke();
OnDocked = null;
WasDocked = true;
DockingTarget.Docked = true;
}
public void Lock(bool isNetworkMessage, bool applyEffects = true, bool moveSubs = true)
@@ -988,6 +998,8 @@ namespace Barotrauma.Items.Components
Item.Submarine.EnableObstructedWaypoints(DockingTarget.Item.Submarine);
obstructedWayPointsDisabled = false;
WasDocked = false;
DockingTarget.WasDocked = false;
DockingTarget.Undock();
DockingTarget = null;
@@ -1052,6 +1064,16 @@ namespace Barotrauma.Items.Components
public override void Update(float deltaTime, Camera cam)
{
//PRETTY HACKY:
//the docking port was docked on the previous round, but not any more -
//must mean that whatever it was docked to (e.g. some enemy sub or respawn shuttle) no longer exists
//let's send an "on_undock" signal so circuits can react to the undocking that never "actually" happened
if (!docked && WasDocked)
{
item.SendSignal("1", "on_undock");
WasDocked = false;
}
dockingCooldown -= deltaTime;
if (DockingTarget == null)
{
@@ -1208,19 +1230,21 @@ namespace Barotrauma.Items.Components
}
}
if (!item.linkedTo.Any()) { return; }
List<MapEntity> linked = new List<MapEntity>(item.linkedTo);
foreach (MapEntity entity in linked)
{
if (!(entity is Item linkedItem)) { continue; }
var dockingPort = linkedItem.GetComponent<DockingPort>();
if (dockingPort != null)
if (item.linkedTo.Any())
{
List<MapEntity> linked = new List<MapEntity>(item.linkedTo);
foreach (MapEntity entity in linked)
{
Dock(dockingPort);
}
if (entity is not Item linkedItem) { continue; }
var dockingPort = linkedItem.GetComponent<DockingPort>();
if (dockingPort != null)
{
Dock(dockingPort);
}
}
}
}
public override void ReceiveSignal(Signal signal, Connection connection)
@@ -154,6 +154,13 @@ namespace Barotrauma.Items.Components
}
}
[Serialize("0,0", IsPropertySaveable.Yes)]
public Point DisallowAttachingOverSize
{
get;
set;
}
[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
{
@@ -496,13 +503,19 @@ namespace Barotrauma.Items.Components
Vector2 diff = new Vector2(
(heldHand.SimPosition.X - arm.SimPosition.X) / 2f,
(heldHand.SimPosition.Y - arm.SimPosition.Y) / 2.5f);
item.SetTransform(heldHand.SimPosition + diff, 0.0f);
//we have forced the item to be in the same sub as the dropper above,
//and are placing it to the position of the hands in "local" coordinates
//which may be outside the sub if the character is e.g. standing half-way through the airlock
// -> let's use the forceSubmarine argument ensure the item is still considered to be in the sub's coordinate space,
// or it will end up in a weird state and seemingly disappear
item.SetTransform(heldHand.SimPosition + diff, 0.0f, forceSubmarine: picker.Submarine);
}
else
{
item.SetTransform(picker.SimPosition, 0.0f);
}
}
item.SetTransform(picker.SimPosition, 0.0f, forceSubmarine: picker.Submarine);
}
}
}
picker.Inventory.RemoveItem(item);
@@ -621,17 +634,34 @@ namespace Barotrauma.Items.Components
if (disallowAttachingOverTags.Any() || !AllowAttachInsideDoors)
{
var connectedHulls = item.CurrentHull?.GetConnectedHulls(includingThis: true, searchDepth: 5, ignoreClosedGaps: true);
Vector2 size = item.Rect.Size.ToVector2() / 2;
Vector2 size = DisallowAttachingOverSize == Point.Zero ?
item.Rect.Size.ToVector2() :
DisallowAttachingOverSize.ToVector2() * item.Scale;
size /= 2f;
foreach (Item otherItem in Item.ItemList)
{
if (otherItem == item || otherItem.body is { BodyType: BodyType.Dynamic, Enabled: true }) { continue; }
if (connectedHulls != null && !connectedHulls.Contains(otherItem.CurrentHull)) { continue; }
if (disallowAttachingOverTags.None(tag => otherItem.HasTag(tag)) &&
if (disallowAttachingOverTags.None(otherItem.HasTag) &&
(otherItem.GetComponent<Door>() == null || AllowAttachInsideDoors))
{
continue;
}
Rectangle worldRect = otherItem.WorldRect;
if (otherItem.GetComponent<Holdable>() is Holdable otherHoldable)
{
if (!otherHoldable.attached) { continue; }
if (otherHoldable.DisallowAttachingOverSize != Point.Zero)
{
Vector2 scaledSize = otherHoldable.DisallowAttachingOverSize.ToVector2() * item.Scale;
worldRect = new Rectangle(
otherItem.WorldPosition.ToPoint() - new Point((int)(scaledSize.X / 2), (int)(-scaledSize.Y / 2)),
scaledSize.ToPoint());
}
}
if (attachPos.X + size.X < worldRect.X || attachPos.X - size.X > worldRect.Right) { continue; }
if (attachPos.Y - size.Y > worldRect.Y || attachPos.Y + size.Y < worldRect.Y - worldRect.Height) { continue; }
tempOverlappingItems.Add(otherItem);
@@ -538,8 +538,8 @@ namespace Barotrauma.Items.Components
}
if (targetEntity != null)
{
ApplyStatusEffects(conditionalActionType, 1.0f, targetCharacter, targetLimb, useTarget: targetEntity, user: user, afflictionMultiplier: damageMultiplier);
ApplyStatusEffects(ActionType.OnUse, 1.0f, targetCharacter, targetLimb, useTarget: targetEntity, user: user, afflictionMultiplier: damageMultiplier);
ApplyStatusEffects(conditionalActionType, 1.0f, targetCharacter, targetLimb, useTarget: targetEntity, user: user, attackMultiplier: damageMultiplier);
ApplyStatusEffects(ActionType.OnUse, 1.0f, targetCharacter, targetLimb, useTarget: targetEntity, user: user, attackMultiplier: damageMultiplier);
}
if (DeleteOnUse)
@@ -920,7 +920,8 @@ namespace Barotrauma.Items.Components
}
}
public void ApplyStatusEffects(ActionType type, float deltaTime, Character character = null, Limb targetLimb = null, Entity useTarget = null, Character user = null, Vector2? worldPosition = null, float afflictionMultiplier = 1.0f)
/// <param name="attackMultiplier">Multiplier used on afflictions caused by the status effects, except ones that <see cref="AfflictionPrefab.AffectedByAttackMultipliers">have been configured to not be affected by attack multipliers.</see></param>
public void ApplyStatusEffects(ActionType type, float deltaTime, Character character = null, Limb targetLimb = null, Entity useTarget = null, Character user = null, Vector2? worldPosition = null, float attackMultiplier = 1.0f)
{
if (statusEffectLists == null) { return; }
@@ -932,7 +933,7 @@ namespace Barotrauma.Items.Components
{
if (broken && !effect.AllowWhenBroken && effect.type != ActionType.OnBroken) { continue; }
if (user != null) { effect.SetUser(user); }
effect.AfflictionMultiplier = afflictionMultiplier;
effect.AttackMultiplier = attackMultiplier;
var c = character;
if (user != null && effect.HasTargetType(StatusEffect.TargetType.Character) && !effect.HasTargetType(StatusEffect.TargetType.UseTarget))
{
@@ -940,7 +941,7 @@ namespace Barotrauma.Items.Components
c = user;
}
item.ApplyStatusEffect(effect, type, deltaTime, c, targetLimb, useTarget, isNetworkEvent: false, checkCondition: false, worldPosition);
effect.AfflictionMultiplier = 1.0f;
effect.AttackMultiplier = 1.0f;
reducesCondition |= effect.ReducesItemCondition();
}
//if any of the effects reduce the item's condition, set the user for OnBroken effects as well
@@ -832,7 +832,7 @@ namespace Barotrauma.Items.Components
public float FabricationDegreeOfSuccess(Character character, ImmutableArray<Skill> skills)
{
if (skills.Length == 0) { return 1.0f; }
if (skills.Length == 0) { return 0.5f; }
if (character == null) { return 0.0f; }
float minDegreeOfSuccess = 1.0f;
@@ -0,0 +1,46 @@
using Barotrauma.Extensions;
using System;
using System.Collections.Generic;
namespace Barotrauma.Items.Components
{
/// <summary>
/// Makes the item inherit the condition from a linked wall or multiple - or in other words, makes it essentially treat the health of the wall as its own health.
/// The wall section with the most damage determines the condition (i.e. the item will be fully broken if there's at least one fully broken wall section).
/// </summary>
class InheritConditionFromLinkedWall(Item item, ContentXElement element) : ItemComponent(item, element)
{
private readonly List<Structure> linkedWalls = [];
public override void OnMapLoaded()
{
foreach (var linkedTo in item.linkedTo)
{
if (linkedTo is Structure structure &&
structure.HasBody)
{
linkedWalls.Add(structure);
structure.OnHealthChanged += (_, _) => UpdateCondition();
}
}
if (linkedWalls.None())
{
DebugConsole.AddWarning($"The item {item.Name} ({item.Prefab.Identifier}) is not linked to any walls with a physics body. The {nameof(InheritConditionFromLinkedWall)} component will do nothing.");
}
}
private void UpdateCondition()
{
float lowestHealthPercent = 1.0f;
foreach (var wall in linkedWalls)
{
foreach (var section in wall.Sections)
{
lowestHealthPercent = Math.Min(lowestHealthPercent, 1.0f - section.damage / wall.MaxHealth);
}
}
item.Condition = item.MaxCondition * lowestHealthPercent;
}
}
}
@@ -136,6 +136,7 @@ namespace Barotrauma.Items.Components
item.CurrentHull.GetLinkedHulls(linkedHulls, includeHiddenHulls: true);
foreach (var linkedHull in linkedHulls)
{
if (linkedHull == item.CurrentHull) { continue; }
hullWaterVolume += linkedHull.WaterVolume;
totalHullVolume += linkedHull.Volume;
}
@@ -148,7 +149,7 @@ namespace Barotrauma.Items.Components
if (!IsActive || Disabled) { return; }
if (flowPercentage <= 0f && item.CurrentHull.WaterVolume <= 0f) { return; }
float powerFactor = Math.Min(currPowerConsumption <= 0.0f || MinVoltage <= 0.0f ? 1.0f : Voltage, MaxOverVoltageFactor);
float powerFactor = Math.Min(PowerConsumption <= 0.0f || MinVoltage <= 0.0f ? 1.0f : Voltage, MaxOverVoltageFactor);
currFlow = flowPercentage / 100.0f * MaxFlow * powerFactor;
if (item.GetComponent<Repairable>() is { IsTinkering: true } repairable)
@@ -247,9 +247,13 @@ namespace Barotrauma.Items.Components
}
}
bool fissionRateControlledBySignals = signalControlledTargetFissionRate.HasValue && lastReceivedFissionRateSignalTime > Timing.TotalTime - 1;
bool turbineOutputRateControlledBySignals = signalControlledTargetTurbineOutput.HasValue && lastReceivedTurbineOutputSignalTime > Timing.TotalTime - 1;
//rapidly adjust the reactor in the first few seconds of the round to prevent overvoltages if the load changed between rounds
//(unless the reactor is being operated by a player)
if (GameMain.GameSession is { RoundDuration: <5 } && lastUser is not { IsPlayer: true })
if (GameMain.GameSession is { RoundDuration: < 5 } && lastUser is not { IsPlayer: true } && PowerOn && AutoTemp &&
!fissionRateControlledBySignals && !turbineOutputRateControlledBySignals)
{
UpdateAutoTemp(100.0f, (float)(Timing.Step * 10.0f));
}
@@ -263,7 +267,7 @@ namespace Barotrauma.Items.Components
float maxPowerOut = GetMaxOutput();
if (signalControlledTargetFissionRate.HasValue && lastReceivedFissionRateSignalTime > Timing.TotalTime - 1)
if (fissionRateControlledBySignals)
{
TargetFissionRate = adjustValueWithoutOverShooting(TargetFissionRate, signalControlledTargetFissionRate.Value, deltaTime * 5.0f);
#if CLIENT
@@ -274,7 +278,7 @@ namespace Barotrauma.Items.Components
{
signalControlledTargetFissionRate = null;
}
if (signalControlledTargetTurbineOutput.HasValue && lastReceivedTurbineOutputSignalTime > Timing.TotalTime - 1)
if (turbineOutputRateControlledBySignals)
{
TargetTurbineOutput = adjustValueWithoutOverShooting(TargetTurbineOutput, signalControlledTargetTurbineOutput.Value, deltaTime * 5.0f);
#if CLIENT
@@ -641,8 +641,7 @@ namespace Barotrauma.Items.Components
for (int i = 0; i < hits.Count; i++)
{
var h = hits[i];
item.SetTransform(h.Point, rotation);
item.Submarine = h.Submarine;
item.SetTransform(h.Point, rotation, forceSubmarine: h.Submarine);
item.UpdateTransform();
if (HandleProjectileCollision(h.Fixture, h.Normal, Vector2.Zero))
{
@@ -103,7 +103,7 @@ namespace Barotrauma.Items.Components
var inputBuilder = ImmutableArray.CreateBuilder<CircuitBoxInputConnection>();
var outputBuilder = ImmutableArray.CreateBuilder<CircuitBoxOutputConnection>();
foreach (Connection conn in Item.Connections)
foreach (Connection conn in Item.Connections.OrderBy(static c => c.DisplayOrder))
{
if (conn.IsOutput)
{
@@ -236,9 +236,7 @@ namespace Barotrauma.Items.Components
cloneNode.ReplaceAllConnectionLabelOverrides(origNode.ConnectionLabelOverrides);
}
if (!clonedContainedItems.Any()) { return; }
foreach (var origComp in original.Components)
foreach (CircuitBoxComponent origComp in original.Components)
{
if (!clonedContainedItems.TryGetValue(origComp.Item.ID, out var clonedItem)) { continue; }
var newComponent = new CircuitBoxComponent(origComp.ID, clonedItem, origComp.Position, this, origComp.UsedResource);
@@ -661,6 +659,7 @@ namespace Barotrauma.Items.Components
}
wire.From.Connection.CircuitBoxConnections.Remove(wire.To);
wire.To.Connection.CircuitBoxConnections.Remove(wire.From);
if (wire.From is CircuitBoxInputConnection input)
{
@@ -17,6 +17,8 @@ namespace Barotrauma.Items.Components
//how many wires can be linked to this connection in total
public readonly int MaxWires = 5;
public readonly int DisplayOrder;
public readonly string Name;
private readonly LocalizedString _displayName;
public LocalizedString DisplayName
@@ -92,7 +94,7 @@ namespace Barotrauma.Items.Components
return "Connection (" + item.Name + ", " + Name + ")";
}
public Connection(ContentXElement element, ConnectionPanel connectionPanel, IdRemap idRemap)
public Connection(ContentXElement element, int connectionIndex, ConnectionPanel connectionPanel, IdRemap idRemap, bool isItemSwap)
{
#if CLIENT
@@ -117,25 +119,44 @@ namespace Barotrauma.Items.Components
IsOutput = element.Name.ToString() == "output";
Name = element.GetAttributeString("name", IsOutput ? "output" : "input");
int displayOrder;
if (element.GetAttribute("displayorderoverride") is not { } displayOrderAttr)
{
var sameElements = connectionPanel.Connections.Where(c => c.IsOutput == IsOutput);
displayOrder = !sameElements.Any() ? 0 : sameElements.Max(static c => c.DisplayOrder) + 1;
}
else
{
displayOrder = displayOrderAttr.GetAttributeInt(0);
}
DisplayOrder = displayOrder;
string displayNameTag = "", fallbackTag = "";
//if displayname is not present, attempt to find it from the prefab
if (element.GetAttribute("displayname") == null)
{
foreach (var subElement in item.Prefab.ConfigElement.Elements())
{
if (!subElement.Name.ToString().Equals("connectionpanel", StringComparison.OrdinalIgnoreCase)) { continue; }
if (!subElement.Name.ToString().Equals("connectionpanel", StringComparison.OrdinalIgnoreCase)) { continue; }
int prefabConnectionIndex = 0;
foreach (XElement connectionElement in subElement.Elements())
{
string prefabConnectionName = connectionElement.GetAttributeString("name", null);
if (prefabConnectionName.IsNullOrEmpty()) { continue; }
string[] aliases = connectionElement.GetAttributeStringArray("aliases", Array.Empty<string>());
if (prefabConnectionName == Name || aliases.Contains(Name))
if (prefabConnectionName == Name || aliases.Contains(Name) ||
//when swapping items, we move wires based on the order of the connections, not the names
//= we should find a connection based on the index if the name doesn't match
(isItemSwap && connectionIndex == prefabConnectionIndex))
{
displayNameTag = connectionElement.GetAttributeString("displayname", "");
fallbackTag = connectionElement.GetAttributeString("fallbackdisplayname", "");
}
prefabConnectionIndex++;
}
}
}
}
else
{
@@ -78,10 +78,10 @@ namespace Barotrauma.Items.Components
switch (subElement.Name.ToString())
{
case "input":
Connections.Add(new Connection(subElement, this, IdRemap.DiscardId));
Connections.Add(new Connection(subElement, connectionIndex: Connections.Count, this, IdRemap.DiscardId, isItemSwap: false));
break;
case "output":
Connections.Add(new Connection(subElement, this, IdRemap.DiscardId));
Connections.Add(new Connection(subElement, connectionIndex: Connections.Count, this, IdRemap.DiscardId, isItemSwap: false));
break;
}
}
@@ -293,10 +293,10 @@ namespace Barotrauma.Items.Components
switch (subElement.Name.ToString())
{
case "input":
loadedConnections.Add(new Connection(subElement, this, idRemap));
loadedConnections.Add(new Connection(subElement, connectionIndex: loadedConnections.Count, this, idRemap, isItemSwap));
break;
case "output":
loadedConnections.Add(new Connection(subElement, this, idRemap));
loadedConnections.Add(new Connection(subElement, connectionIndex: loadedConnections.Count, this, idRemap, isItemSwap));
break;
}
}
@@ -1,4 +1,5 @@
using System;
using Barotrauma.Networking;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -7,7 +8,7 @@ namespace Barotrauma.Items.Components;
/// <summary>
/// Base class for signal components that can select between input/output connections (e.g. multiplexer and demultiplexer components)
/// </summary>
abstract class ConnectionSelectorComponent : ItemComponent
abstract partial class ConnectionSelectorComponent : ItemComponent, IServerSerializable
{
protected int selectedConnectionIndex;
protected string selectedConnectionIndexStr;
@@ -22,6 +23,8 @@ abstract class ConnectionSelectorComponent : ItemComponent
get { return selectedConnectionIndex; }
set
{
int prevIndex = selectedConnectionIndex; // store original, so we know if the state has changed and can sync it in MP
selectedConnectionIndex = Math.Max(0, value);
//don't clamp until we've determined how many connections the item has
//(can't be done until the connection panel component has been loaded too)
@@ -31,6 +34,11 @@ abstract class ConnectionSelectorComponent : ItemComponent
}
selectedConnectionName = GetConnectionName(selectedConnectionIndex);
selectedConnectionIndexStr = selectedConnectionIndex.ToString();
if (prevIndex != selectedConnectionIndex)
{
OnStateChanged();
}
}
}
@@ -55,6 +63,8 @@ abstract class ConnectionSelectorComponent : ItemComponent
{
}
partial void OnStateChanged();
protected abstract string GetConnectionName(int connectionIndex);
/// <summary>
@@ -63,10 +63,7 @@ namespace Barotrauma.Items.Components
/// This can be used to make them additionally work the other way around, periodically getting the current value of the property from the item and refreshing the UI.
/// </summary>
public float GetValueInterval { get; set; } = -1.0f;
#if CLIENT
public float GetValueTimer;
#endif
public string Name => "CustomInterfaceElement";
@@ -248,7 +245,7 @@ namespace Barotrauma.Items.Components
ciElement.Label = "Signal out " + customInterfaceElementList.Count(e => e.ContinuousSignal == ciElement.ContinuousSignal);
}
customInterfaceElementList.Add(ciElement);
IsActive |= ciElement.ContinuousSignal;
IsActive |= ciElement.ContinuousSignal || ciElement.GetValueInterval > 0.0f;
}
InitProjSpecific();
@@ -348,13 +345,10 @@ namespace Barotrauma.Items.Components
//make sure the clients know about the states of the checkboxes and text fields
if (customInterfaceElementList.Any())
{
if (item.FullyInitialized)
CoroutineManager.Invoke(() =>
{
CoroutineManager.Invoke(() =>
{
if (!item.Removed) { item.CreateServerEvent(this); }
}, delay: 0.1f);
}
if (item.FullyInitialized && !item.Removed) { item.CreateServerEvent(this); }
}, delay: 0.1f);
}
#endif
}
@@ -418,6 +412,16 @@ namespace Barotrauma.Items.Components
{
foreach (CustomInterfaceElement ciElement in customInterfaceElementList)
{
if (ciElement.GetValueInterval > 0.0f)
{
ciElement.GetValueTimer -= deltaTime;
if (ciElement.GetValueTimer <= 0.0f)
{
SetSignalToPropertyValue(ciElement);
ciElement.GetValueTimer = ciElement.GetValueInterval;
}
}
if (!ciElement.ContinuousSignal && ciElement.PropertyName != "Voltage") { continue; }
//TODO: allow changing output when a tickbox is not selected
if (!string.IsNullOrEmpty(ciElement.Signal) && ciElement.Connection != null)
@@ -140,7 +140,7 @@ namespace Barotrauma.Items.Components
}
}
[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.")]
[Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f, DecimalCount = 2), Serialize(0.0f, IsPropertySaveable.Yes, description: "How much light pulsates. 0 = not at all, 1 = alternates between full brightness and off.")]
public float PulseAmount
{
get { return pulseAmount; }
@@ -268,7 +268,7 @@ namespace Barotrauma.Items.Components
public float RotationSpeedHighSkill { get; private set; }
[Serialize("0,0,0,0", IsPropertySaveable.Yes, description: "Optional screen tint color when the item is being operated (R,G,B,A)."),
Editable]
Editable(TransferToSwappedItem = true)]
public Color HudTint { get; set; }
[Header(localizedTextTag: "sp.turret.AutoOperate.propertyheader")]
@@ -461,10 +461,25 @@ namespace Barotrauma
}
}
public float ImpactTolerance => Prefab.ImpactTolerance;
private float impactTolerance;
[Serialize(0.0f, IsPropertySaveable.No), ConditionallyEditable(ConditionallyEditable.ConditionType.ReceivesSubmarineImpacts, MinValueFloat = 0, MaxValueFloat = 100)]
public float ImpactTolerance
{
get { return impactTolerance; }
set { impactTolerance = Math.Max(value, 0.0f); }
}
public float ImpactDamage => Prefab.ImpactDamage;
public float ImpactDamageProbability => Prefab.ImpactDamageProbability;
[Serialize(0.0f, IsPropertySaveable.No, description: "The amount of damage the item takes from impacts. Acts as a multiplier on the strength of the impact. Note that ImpactTolerance must be set for impacts to register."),
ConditionallyEditable(ConditionallyEditable.ConditionType.ReceivesSubmarineImpacts, MinValueFloat = 0, MaxValueFloat = 100)]
public float ImpactDamage { get; set; }
[Serialize(1.0f, IsPropertySaveable.No, description: "Probability for impacts to register. Defaults to 1. Note that ImpactTolerance must also be set for impacts to register."),
ConditionallyEditable(ConditionallyEditable.ConditionType.ReceivesSubmarineImpacts, MinValueFloat = 0, MaxValueFloat = 1)]
public float ImpactDamageProbability { get; set; }
public const float SubmarineImpactCooldown = 0.1f;
public double LastSubmarineImpactTime;
public float InteractDistance => Prefab.InteractDistance;
@@ -1556,7 +1571,7 @@ namespace Barotrauma
if (!updateableComponents.Contains(component))
{
updateableComponents.Add(component);
this.isActive = true;
this.IsActive = true;
}
}
};
@@ -1647,7 +1662,19 @@ namespace Barotrauma
contained.Container = null;
}
public void SetTransform(Vector2 simPosition, float rotation, bool findNewHull = true, bool setPrevTransform = true)
/// <summary>
/// Sets the position and rotation of the item, and its physics body if it has one.
/// </summary>
/// <param name="simPosition">Position in simulation units.</param>
/// <param name="rotation">Rotation in radians</param>
/// <param name="findNewHull">Should the hull the item is inside be immediately updated? Generally only useful to set to false
/// for performance reasons when finding the hull is unnecessary (e.g. if it's being forced to something after setting the transform).</param>
/// <param name="setPrevTransform">Should the previous transform of the item be immediately set to the new one?
/// The previous transform is used to interpolate draw positions/rotations, and you should generally only set this to false if
/// you're trying to simulate movement instead of simply teleporting the item somewhere.</param>
/// <param name="forceSubmarine">If you know the position is in a specific sub's coordinate space and want to ensure the item
/// is still considered to be in that sub even if the transform ended up outside hulls.</param>
public void SetTransform(Vector2 simPosition, float rotation, bool findNewHull = true, bool setPrevTransform = true, Submarine forceSubmarine = null)
{
if (!MathUtils.IsValid(simPosition))
{
@@ -1685,6 +1712,7 @@ namespace Barotrauma
rect.Y = (int)MathF.Round(displayPos.Y + rect.Height / 2.0f);
if (findNewHull) { FindHull(); }
if (forceSubmarine != null) { Submarine = forceSubmarine; }
}
/// <summary>
@@ -1856,7 +1884,7 @@ namespace Barotrauma
if (newRootContainer != RootContainer)
{
RootContainer = newRootContainer;
isActive = true;
IsActive = true;
foreach (Item containedItem in ContainedItems)
{
containedItem.RefreshRootContainer();
@@ -2371,12 +2399,16 @@ namespace Barotrauma
}
}
private bool isActive = true;
/// <summary>
/// Inactive items are not updated. Note that actions such as dropping can reactivate the item, and that the item can go inactive by itself if it no longer needs updating;
/// </summary>
public bool IsActive = true;
public bool IsInRemoveQueue;
public override void Update(float deltaTime, Camera cam)
{
if (!isActive || IsLayerHidden || IsInRemoveQueue) { return; }
if (!IsActive || IsLayerHidden || IsInRemoveQueue) { return; }
if (impactQueue != null)
{
@@ -2542,7 +2574,7 @@ namespace Barotrauma
#if CLIENT
positionBuffer.Clear();
#endif
isActive = false;
IsActive = false;
}
}
@@ -2703,7 +2735,7 @@ namespace Barotrauma
impactQueue.Enqueue(impact);
}
isActive = true;
IsActive = true;
return true;
}
@@ -3484,7 +3516,7 @@ namespace Barotrauma
if (body != null)
{
isActive = true;
IsActive = true;
body.Enabled = true;
body.PhysEnabled = true;
body.ResetDynamics();
@@ -3624,7 +3656,7 @@ namespace Barotrauma
item.body.Enabled = item.body.PhysEnabled = isFirst;
if (isFirst)
{
item.isActive = true;
item.IsActive = true;
item.body.ResetDynamics();
}
}
@@ -4385,13 +4417,18 @@ namespace Barotrauma
{
foreach (var connection in thisConnectionPanel.Connections)
{
var newConnection = newConnectionPanel.Connections.FirstOrDefault(c => c.Name == connection.Name);
if (newConnection == null) { continue; }
foreach (var wire in connection.Wires)
{
int connectionIndex = wire.Connections.IndexOf(connection);
int wireConnectionIndex = wire.Connections.IndexOf(connection);
wire.RemoveConnection(this);
wire.Connect(newConnection, connectionIndex, addNode: false);
int thisConnectionIndex = connection.ConnectionPanel.Connections.IndexOf(connection);
if (thisConnectionIndex < 0 || thisConnectionIndex >= newConnectionPanel.Connections.Count)
{
DebugConsole.AddWarning($"Failed to move a wire from the connection {connection.Name} when swapping the item {Name} with {newItem.Name}. The new item probably does not have the same number of connections as the previous one.");
continue;
}
Connection newConnection = newConnectionPanel.Connections[thisConnectionIndex];
wire.Connect(newConnection, wireConnectionIndex, addNode: false);
newConnection.ConnectWire(wire);
}
}
@@ -813,20 +813,6 @@ namespace Barotrauma
[Serialize(false, IsPropertySaveable.No)]
public bool DamagedByMonsters { get; private set; }
private float impactTolerance;
[Serialize(0.0f, IsPropertySaveable.No)]
public float ImpactTolerance
{
get { return impactTolerance; }
set { impactTolerance = Math.Max(value, 0.0f); }
}
[Serialize(0.0f, IsPropertySaveable.No, description: "The amount of damage the item takes from impacts. Acts as a multiplier on the strength of the impact. Note that ImpactTolerance must be set for impacts to register.")]
public float ImpactDamage { get; set; }
[Serialize(1.0f, IsPropertySaveable.No, description: "Probability for impacts to register. Defaults to 1. Note that ImpactTolerance must also be set for impacts to register.")]
public float ImpactDamageProbability { get; set; }
[Serialize(false, IsPropertySaveable.No, "If true, submarine impacts will trigger OnImpact effects. Only applies to items with a null or non-dynamic physics body - items with dynamic bodies always react to impacts.")]
public bool ReceiveSubmarineImpacts { get; set; }