This commit is contained in:
EvilFactory
2023-06-15 12:13:50 -03:00
210 changed files with 4491 additions and 2580 deletions
@@ -1,13 +1,15 @@
using Barotrauma.Networking;
using Barotrauma.IO;
using Barotrauma.Networking;
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Joints;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using Barotrauma.IO;
using System.Linq;
using System.Xml.Linq;
#if CLIENT
using Barotrauma.Lights;
#endif
namespace Barotrauma.Items.Components
{
@@ -243,10 +245,12 @@ namespace Barotrauma.Items.Components
if (!target.item.Submarine.DockedTo.Contains(item.Submarine))
{
target.item.Submarine.ConnectedDockingPorts.Add(item.Submarine, target);
target.item.Submarine.RefreshConnectedSubs();
}
if (!item.Submarine.DockedTo.Contains(target.item.Submarine))
{
item.Submarine.ConnectedDockingPorts.Add(target.item.Submarine, this);
item.Submarine.RefreshConnectedSubs();
}
DockingTarget = target;
@@ -507,9 +511,9 @@ namespace Barotrauma.Items.Components
wire.RemoveConnection(DockingTarget.item);
powerConnection.TryAddLink(wire);
wire.Connect(powerConnection, false, false);
wire.TryConnect(powerConnection, addNode: false);
recipient.TryAddLink(wire);
wire.Connect(recipient, false, false);
wire.TryConnect(recipient, addNode: false);
//Flag connections to be updated
Powered.ChangedConnections.Add(powerConnection);
@@ -558,6 +562,7 @@ namespace Barotrauma.Items.Components
var subs = new Submarine[] { item.Submarine, DockingTarget.item.Submarine };
bodies = new Body[4];
RemoveConvexHulls();
if (DockingTarget.Door != null)
{
@@ -648,8 +653,10 @@ namespace Barotrauma.Items.Components
hullRects[i].X -= expand;
hullRects[i].Width += expand * 2;
hullRects[i].Location -= MathUtils.ToPoint(subs[i].WorldPosition - subs[i].HiddenSubPosition);
hulls[i] = new Hull(hullRects[i], subs[i]);
hulls[i].RoomName = IsHorizontal ? "entityname.dockingport" : "entityname.dockinghatch";
hulls[i] = new Hull(hullRects[i], subs[i])
{
RoomName = IsHorizontal ? "entityname.dockingport" : "entityname.dockinghatch"
};
hulls[i].AddToGrid(subs[i]);
hulls[i].FreeID();
@@ -661,6 +668,15 @@ namespace Barotrauma.Items.Components
BodyType.Static);
}
}
#if CLIENT
for (int i = 0; i < 2; i++)
{
convexHulls[i] =
new ConvexHull(new Rectangle(
new Point((int)item.Position.X, item.Rect.Y - item.Rect.Height * i),
new Point((int)(DockingTarget.item.WorldPosition.X - item.WorldPosition.X), 0)), IsHorizontal, item);
}
#endif
if (rightHullDiff <= 100 && hulls[0].Submarine != null)
{
@@ -764,15 +780,17 @@ namespace Barotrauma.Items.Components
hullRects[1].Height += midHullDiff / 2 + 1;
}
int expand = 5;
for (int i = 0; i < 2; i++)
{
hullRects[i].Y += expand;
hullRects[i].Height += expand * 2;
hullRects[i].Location -= MathUtils.ToPoint(subs[i].WorldPosition - subs[i].HiddenSubPosition);
hulls[i] = new Hull(hullRects[i], subs[i]);
hulls[i].RoomName = IsHorizontal ? "entityname.dockingport" : "entityname.dockinghatch";
hulls[i] = new Hull(hullRects[i], subs[i])
{
RoomName = IsHorizontal ? "entityname.dockingport" : "entityname.dockinghatch",
AvoidStaying = true
};
hulls[i].AddToGrid(subs[i]);
hulls[i].FreeID();
@@ -784,6 +802,15 @@ namespace Barotrauma.Items.Components
BodyType.Static);
}
}
#if CLIENT
for (int i = 0; i < 2; i++)
{
convexHulls[i] =
new ConvexHull(new Rectangle(
new Point(item.Rect.X + item.Rect.Width * i, (int)item.Position.Y),
new Point(0, (int)(DockingTarget.item.WorldPosition.Y - item.WorldPosition.Y))), IsHorizontal, item);
}
#endif
if (midHullDiff <= 100 && hulls[0].Submarine != null)
{
@@ -822,6 +849,8 @@ namespace Barotrauma.Items.Components
}
}
partial void RemoveConvexHulls();
private void LinkHullsToGaps()
{
if (gap == null || hulls == null || hulls[0] == null || hulls[1] == null)
@@ -916,7 +945,9 @@ namespace Barotrauma.Items.Components
}
DockingTarget.item.Submarine.ConnectedDockingPorts.Remove(item.Submarine);
DockingTarget.item.Submarine.RefreshConnectedSubs();
item.Submarine.ConnectedDockingPorts.Remove(DockingTarget.item.Submarine);
item.Submarine.RefreshConnectedSubs();
if (Door != null && DockingTarget.Door != null)
{
@@ -976,6 +1007,8 @@ namespace Barotrauma.Items.Components
hulls[0]?.Remove(); hulls[0] = null;
hulls[1]?.Remove(); hulls[1] = null;
RemoveConvexHulls();
if (gap != null)
{
gap.Remove();
@@ -1091,6 +1124,7 @@ namespace Barotrauma.Items.Components
hulls[0]?.Remove(); hulls[0] = null;
hulls[1]?.Remove(); hulls[1] = null;
gap?.Remove(); gap = null;
RemoveConvexHulls();
overlaySprite?.Remove();
overlaySprite = null;
@@ -69,7 +69,7 @@ namespace Barotrauma.Items.Components
private bool isBroken;
public bool CanBeTraversed => (IsOpen || IsBroken) && !IsJammed && !IsStuck && !Impassable;
public bool CanBeTraversed => !Impassable && (IsBroken || IsOpen);
public bool IsBroken
{
@@ -186,13 +186,19 @@ namespace Barotrauma.Items.Components
{
get { return openState; }
set
{
{
openState = MathHelper.Clamp(value, 0.0f, 1.0f);
#if CLIENT
float size = IsHorizontal ? item.Rect.Width : item.Rect.Height;
if (Math.Abs(lastConvexHullState - openState) * size < 5.0f) { return; }
UpdateConvexHulls();
lastConvexHullState = openState;
//refresh convex hulls if the body of the door has moved by 5 pixels,
//or if it becomes fully closed or fully open
if (Math.Abs(lastConvexHullState - openState) * size > 5.0f ||
(openState <= 0.0f && lastConvexHullState > 0.0f) ||
(openState >= 1.0f && lastConvexHullState < 1.0f))
{
UpdateConvexHulls();
lastConvexHullState = openState;
}
#endif
}
}
@@ -523,11 +529,11 @@ namespace Barotrauma.Items.Components
{
RefreshLinkedGap();
#if CLIENT
Vector2[] corners = GetConvexHullCorners(Rectangle.Empty);
convexHull = new ConvexHull(corners, Color.Black, item);
if (Window != Rectangle.Empty) convexHull2 = new ConvexHull(corners, Color.Black, item);
convexHull = new ConvexHull(doorRect, IsHorizontal, item);
if (Window != Rectangle.Empty)
{
convexHull2 = new ConvexHull(doorRect, IsHorizontal, item);
}
UpdateConvexHulls();
#endif
}
@@ -1,10 +1,9 @@
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using System.Linq;
using Barotrauma.Extensions;
using Barotrauma.Extensions;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
@@ -96,11 +95,16 @@ namespace Barotrauma.Items.Components
if (selectedEffect != null)
{
targetCharacter = character;
ApplyStatusEffects(ActionType.OnWearing, 1.0f);
ApplyStatusEffects(ActionType.OnWearing, 1.0f, targetCharacter);
float selectedEffectStrength = GetCombinedEffectStrength();
character.CharacterHealth.ApplyAffliction(null, selectedEffect.Instantiate(selectedEffectStrength));
var affliction = character.CharacterHealth.GetAllAfflictions().FirstOrDefault(a => a.Prefab == selectedEffect);
if (affliction != null) { affliction.Strength = selectedEffectStrength; }
if (affliction != null)
{
affliction.Strength = selectedEffectStrength;
//force strength to the correct value to bypass any clamping e.g. AfflictionHusk might be doing
affliction.SetStrength(selectedEffectStrength);
}
#if SERVER
item.CreateServerEvent(this);
#endif
@@ -110,7 +114,12 @@ namespace Barotrauma.Items.Components
float selectedTaintedEffectStrength = GetCombinedTaintedEffectStrength();
character.CharacterHealth.ApplyAffliction(null, selectedTaintedEffect.Instantiate(selectedTaintedEffectStrength));
var affliction = character.CharacterHealth.GetAllAfflictions().FirstOrDefault(a => a.Prefab == selectedTaintedEffect);
if (affliction != null) { affliction.Strength = selectedTaintedEffectStrength; }
if (affliction != null)
{
affliction.Strength = selectedTaintedEffectStrength;
//force strength to the correct value to bypass any clamping e.g. AfflictionHusk might be doing
affliction.SetStrength(selectedTaintedEffectStrength);
}
targetCharacter = character;
#if SERVER
item.CreateServerEvent(this);
@@ -127,7 +136,7 @@ namespace Barotrauma.Items.Components
base.Update(deltaTime, cam);
if (targetCharacter != null)
{
var rootContainer = item.GetRootContainer();
var rootContainer = item.RootContainer;
if (!targetCharacter.HasEquippedItem(item) &&
(rootContainer == null || !targetCharacter.HasEquippedItem(rootContainer) || !targetCharacter.Inventory.IsInLimbSlot(rootContainer, InvSlotType.HealthInterface)))
{
@@ -220,7 +229,7 @@ namespace Barotrauma.Items.Components
return MathHelper.Clamp(probability, 0.0f, 1.0f);
}
private float GetTaintedProbabilityOnCombine(Character user)
private static float GetTaintedProbabilityOnCombine(Character user)
{
if (user == null) { return 1.0f; }
float probability = 1.0f - user.GetStatValue(StatTypes.GeneticMaterialTaintedProbabilityReductionOnCombine);
@@ -409,6 +409,7 @@ namespace Barotrauma.Items.Components
private int leafVariants;
private int[] flowerTiles;
[Serialize(100.0f, IsPropertySaveable.Yes)]
public float Health
{
get => health;
@@ -321,12 +321,12 @@ namespace Barotrauma.Items.Components
}
}
public override void Drop(Character dropper)
public override void Drop(Character dropper, bool setTransform = true)
{
Drop(true, dropper);
Drop(true, dropper, setTransform);
}
private void Drop(bool dropConnectedWires, Character dropper)
private void Drop(bool dropConnectedWires, Character dropper, bool setTransform = true)
{
GetRope()?.Snap();
if (dropConnectedWires)
@@ -343,8 +343,11 @@ namespace Barotrauma.Items.Components
DeattachFromWall();
}
if (Pusher != null) { Pusher.Enabled = false; }
if (item.body != null) { item.body.Enabled = true; }
if (setTransform)
{
if (Pusher != null) { Pusher.Enabled = false; }
if (item.body != null) { item.body.Enabled = true; }
}
IsActive = false;
attachTargetCell = null;
@@ -357,7 +360,7 @@ namespace Barotrauma.Items.Components
item.Submarine = picker.Submarine;
if (item.body != null)
if (item.body != null && setTransform)
{
if (item.body.Removed)
{
@@ -599,6 +602,10 @@ namespace Barotrauma.Items.Components
throw new InvalidOperationException($"Tried to attach an item with no physics body to a wall ({item.Prefab.Identifier}).");
}
body.Enabled = false;
body.SetTransformIgnoreContacts(body.SimPosition, rotation: 0.0f);
item.body = null;
//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)
{
@@ -638,9 +645,6 @@ namespace Barotrauma.Items.Components
}
}
body.Enabled = false;
item.body = null;
DisplayMsg = prevMsg;
PickKey = prevPickKey;
requiredItems = new Dictionary<RelatedItem.RelationType, List<RelatedItem>>(prevRequiredItems);
@@ -812,7 +816,7 @@ namespace Barotrauma.Items.Components
foreach (var edge in cell.Edges)
{
if (!edge.IsSolid) { continue; }
if (MathUtils.GetLineIntersection(edge.Point1, edge.Point2, user.WorldPosition, attachPos, out Vector2 intersection))
if (MathUtils.GetLineSegmentIntersection(edge.Point1, edge.Point2, user.WorldPosition, attachPos, out Vector2 intersection))
{
attachPos = intersection;
edgeFound = true;
@@ -97,11 +97,18 @@ namespace Barotrauma.Items.Components
{
if (holdable != null && !holdable.Attached)
{
trigger.Enabled = false;
if (trigger != null)
{
trigger.Enabled = false;
}
IsActive = false;
}
else
{
if (trigger == null)
{
CreateTriggerBody();
}
if (trigger != null && Vector2.DistanceSquared(item.SimPosition, trigger.SimPosition) > 0.01f)
{
trigger.SetTransform(item.SimPosition, 0.0f);
@@ -123,12 +130,15 @@ namespace Barotrauma.Items.Components
{
holdable.PickingTime = float.MaxValue;
}
}
private void CreateTriggerBody()
{
System.Diagnostics.Debug.Assert(trigger == null, "LevelResource trigger already created!");
var body = item.body ?? holdable.Body;
if (body != null)
if (body != null && Attached)
{
trigger = new PhysicsBody(body.Width, body.Height, body.Radius,
trigger = new PhysicsBody(body.Width, body.Height, body.Radius,
body.Density,
BodyType.Static,
Physics.CollisionWall,
@@ -143,7 +153,6 @@ namespace Barotrauma.Items.Components
protected override void RemoveComponentSpecific()
{
base.RemoveComponentSpecific();
if (trigger != null)
{
trigger.Remove();
@@ -170,9 +170,9 @@ namespace Barotrauma.Items.Components
return characterUsable || character == null;
}
public override void Drop(Character dropper)
public override void Drop(Character dropper, bool setTransform = true)
{
base.Drop(dropper);
base.Drop(dropper, setTransform);
hitting = false;
hitPos = 0.0f;
}
@@ -241,12 +241,9 @@ namespace Barotrauma.Items.Components
}
}
public override void Drop(Character dropper)
public override void Drop(Character dropper, bool setTransform = true)
{
if (picker == null)
{
picker = dropper;
}
picker ??= dropper;
Vector2 bodyDropPos = Vector2.Zero;
@@ -255,8 +252,7 @@ namespace Barotrauma.Items.Components
if (item.ParentInventory != null && item.ParentInventory.Owner != null && !item.ParentInventory.Owner.Removed)
{
bodyDropPos = item.ParentInventory.Owner.SimPosition;
if (item.body != null) item.body.ResetDynamics();
item.body?.ResetDynamics();
}
}
else if (!picker.Removed)
@@ -270,7 +266,7 @@ namespace Barotrauma.Items.Components
picker = null;
}
if (item.body != null && !item.body.Enabled)
if (item.body != null && !item.body.Enabled && setTransform)
{
if (item.body.Removed)
{
@@ -912,17 +912,16 @@ namespace Barotrauma.Items.Components
// A general purpose system could be better, but it would most likely require changes in the way we define the status effects in xml.
foreach (ISerializableEntity target in currentTargets)
{
if (!(target is Door door)) { continue; }
if (target is not Door door) { continue; }
if (!door.CanBeWelded || !door.Item.IsInteractable(user)) { continue; }
for (int i = 0; i < effect.propertyNames.Length; i++)
foreach (var propertyEffect in effect.PropertyEffects)
{
Identifier propertyName = effect.propertyNames[i];
if (propertyName != "stuck") { continue; }
if (door.SerializableProperties == null || !door.SerializableProperties.TryGetValue(propertyName, out SerializableProperty property)) { continue; }
if (propertyEffect.propertyName != "stuck") { continue; }
if (door.SerializableProperties == null || !door.SerializableProperties.TryGetValue(propertyEffect.propertyName, out SerializableProperty property)) { continue; }
object value = property.GetValue(target);
if (door.Stuck > 0)
{
bool isCutting = effect.propertyEffects[i].GetType() == typeof(float) && (float)effect.propertyEffects[i] < 0;
bool isCutting = propertyEffect.value is float and < 0;
var progressBar = user.UpdateHUDProgressBar(door, door.Item.WorldPosition, door.Stuck / 100, Color.DarkGray * 0.5f, Color.White,
textTag: isCutting ? "progressbar.cutting" : "progressbar.welding");
if (progressBar != null) { progressBar.Size = new Vector2(60.0f, 20.0f); }
@@ -56,9 +56,9 @@ namespace Barotrauma.Items.Components
return false;
}
public override void Drop(Character dropper)
public override void Drop(Character dropper, bool setTransform = true)
{
base.Drop(dropper);
base.Drop(dropper, setTransform);
throwState = ThrowState.None;
throwAngle = ThrowAngleStart;
Item.ResetWaterDragCoefficient();
@@ -442,7 +442,7 @@ namespace Barotrauma.Items.Components
}
/// <summary>a Character has dropped the item</summary>
public virtual void Drop(Character dropper) { }
public virtual void Drop(Character dropper, bool setTransform = true) { }
/// <returns>true if the operation was completed</returns>
public virtual bool CrewAIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
@@ -724,20 +724,50 @@ namespace Barotrauma.Items.Components
{
if (character.IsBot && item.IgnoreByAI(character)) { return false; }
if (!item.IsInteractable(character)) { return false; }
if (requiredItems.None()) { return true; }
if (character.Inventory != null)
if (requiredItems.Count == 0) { return true; }
if (character.Inventory != null && requiredItems.TryGetValue(RelatedItem.RelationType.Picked, out List<RelatedItem> relatedItems))
{
foreach (Item item in character.Inventory.AllItems)
foreach (RelatedItem relatedItem in relatedItems)
{
if (requiredItems.Any(ri => ri.Value.Any(r => r.Type == RelatedItem.RelationType.Picked && r.MatchesItem(item))))
foreach (Item otherItem in character.Inventory.AllItems)
{
return true;
}
if (relatedItem.MatchesItem(otherItem))
{
if (otherItem.GetComponent<IdCard>() is IdCard idCard)
{
if (!CheckIdCardAccess(relatedItem, idCard))
{
continue;
}
}
return true;
}
}
}
}
return false;
}
/// <summary>
/// Presumes that matching is already checked.
/// </summary>
private bool CheckIdCardAccess(RelatedItem relatedItem, IdCard idCard)
{
if (item.Submarine != null)
{
//id cards don't work in enemy subs (except on items that only require the default "idcard" tag)
if (idCard.TeamID != CharacterTeamType.None && idCard.TeamID != item.Submarine.TeamID && relatedItem.Identifiers.Any(id => id != "idcard"))
{
return false;
}
else if (idCard.SubmarineSpecificID != 0 && item.Submarine.SubmarineSpecificIDTag != idCard.SubmarineSpecificID)
{
return false;
}
}
return true;
}
public virtual bool HasRequiredItems(Character character, bool addMessage, LocalizedString msg = null)
{
if (requiredItems.None()) { return true; }
@@ -773,23 +803,14 @@ namespace Barotrauma.Items.Components
bool CheckItems(RelatedItem relatedItem, IEnumerable<Item> itemList)
{
bool Predicate(Item it)
bool Predicate(Item it)
{
if (it == null || it.Condition <= 0.0f || !relatedItem.MatchesItem(it)) { return false; }
if (item.Submarine != null)
if (it.GetComponent<IdCard>() is IdCard idCard)
{
var idCard = it.GetComponent<IdCard>();
if (idCard != null)
if (!CheckIdCardAccess(relatedItem, idCard))
{
//id cards don't work in enemy subs (except on items that only require the default "idcard" tag)
if (idCard.TeamID != CharacterTeamType.None && idCard.TeamID != item.Submarine.TeamID && relatedItem.Identifiers.Any(id => id != "idcard"))
{
return false;
}
else if (idCard.SubmarineSpecificID != 0 && item.Submarine.SubmarineSpecificIDTag != idCard.SubmarineSpecificID)
{
return false;
}
return false;
}
}
return true;
@@ -1029,7 +1050,7 @@ namespace Barotrauma.Items.Components
prevRequiredItems[newRequiredItem.Type].Find(ri => ri.JoinedIdentifiers == newRequiredItem.JoinedIdentifiers) : null;
if (prevRequiredItem != null)
{
newRequiredItem.statusEffects = prevRequiredItem.statusEffects;
newRequiredItem.StatusEffects = prevRequiredItem.StatusEffects;
newRequiredItem.Msg = prevRequiredItem.Msg;
newRequiredItem.IsOptional = prevRequiredItem.IsOptional;
newRequiredItem.IgnoreInEditor = prevRequiredItem.IgnoreInEditor;
@@ -20,11 +20,13 @@ namespace Barotrauma.Items.Components
{
public readonly int MaxStackSize;
public List<RelatedItem> ContainableItems;
public readonly bool AutoInject;
public SlotRestrictions(int maxStackSize, List<RelatedItem> containableItems)
public SlotRestrictions(int maxStackSize, List<RelatedItem> containableItems, bool autoInject)
{
MaxStackSize = maxStackSize;
ContainableItems = containableItems;
AutoInject = autoInject;
}
public bool MatchesItem(Item item)
@@ -269,7 +271,7 @@ namespace Barotrauma.Items.Components
List<SlotRestrictions> newSlotRestrictions = new List<SlotRestrictions>(totalCapacity);
for (int i = 0; i < capacity; i++)
{
newSlotRestrictions.Add(new SlotRestrictions(maxStackSize, ContainableItems));
newSlotRestrictions.Add(new SlotRestrictions(maxStackSize, ContainableItems, autoInject: false));
}
int subContainerIndex = capacity;
@@ -279,6 +281,7 @@ namespace Barotrauma.Items.Components
int subCapacity = subElement.GetAttributeInt("capacity", 1);
int subMaxStackSize = subElement.GetAttributeInt("maxstacksize", maxStackSize);
bool autoInject = subElement.GetAttributeBool("autoinject", false);
var subContainableItems = new List<RelatedItem>();
foreach (var subSubElement in subElement.Elements())
@@ -298,7 +301,7 @@ namespace Barotrauma.Items.Components
for (int i = subContainerIndex; i < subContainerIndex + subCapacity; i++)
{
newSlotRestrictions.Add(new SlotRestrictions(subMaxStackSize, subContainableItems));
newSlotRestrictions.Add(new SlotRestrictions(subMaxStackSize, subContainableItems, autoInject));
}
subContainerIndex += subCapacity;
}
@@ -351,7 +354,7 @@ namespace Barotrauma.Items.Components
foreach (var containableItem in slotRestrictions[index].ContainableItems)
{
if (!containableItem.MatchesItem(containedItem)) { continue; }
foreach (StatusEffect effect in containableItem.statusEffects)
foreach (StatusEffect effect in containableItem.StatusEffects)
{
activeContainedItems.Add(new ActiveContainedItem(containedItem, effect, containableItem.ExcludeBroken, containableItem.ExcludeFullCondition));
}
@@ -466,7 +469,7 @@ namespace Barotrauma.Items.Components
prevContainedItemPositions = item.Position;
}
if (AutoInject)
if (AutoInject || slotRestrictions.Any(s => s.AutoInject))
{
//normally autoinjection should delete the (medical) item, so it only gets applied once
//but in multiplayer clients aren't allowed to remove items themselves, so they may be able to trigger this dozens of times
@@ -480,7 +483,21 @@ namespace Barotrauma.Items.Components
ownerCharacter.HealthPercentage / 100f <= AutoInjectThreshold &&
ownerCharacter.HasEquippedItem(item))
{
foreach (Item item in Inventory.AllItemsMod)
if (AutoInject)
{
Inventory.AllItemsMod.ForEach(i => Inject(i));
}
else
{
for (int i = 0; i < slotRestrictions.Length; i++)
{
if (slotRestrictions[i].AutoInject)
{
Inventory.GetItemsAt(i).ForEachMod(i => Inject(i));
}
}
}
void Inject(Item item)
{
item.ApplyStatusEffects(ActionType.OnSuccess, 1.0f, ownerCharacter, useTarget: ownerCharacter);
item.ApplyStatusEffects(ActionType.OnUse, 1.0f, ownerCharacter, useTarget: ownerCharacter);
@@ -632,7 +649,7 @@ namespace Barotrauma.Items.Components
return false;
}
public override void Drop(Character dropper)
public override void Drop(Character dropper, bool setTransform = true)
{
IsActive = true;
SetContainedActive(false);
@@ -98,6 +98,20 @@ namespace Barotrauma.Items.Components
set;
}
[Serialize(true, IsPropertySaveable.No, description: "Can another character select this controller when another character has already selected it?")]
public bool AllowSelectingWhenSelectedByOther
{
get;
set;
}
[Serialize(true, IsPropertySaveable.No, description: "Can another character select this controller when a bot has already selected it?")]
public bool AllowSelectingWhenSelectedByBot
{
get;
set;
}
public bool ControlCharacterPose
{
get { return limbPositions.Count > 0; }
@@ -466,8 +480,18 @@ namespace Barotrauma.Items.Components
IsActive = false;
CancelUsing(user);
user = null;
return false;
}
else if (user.IsBot && !activator.IsBot)
{
if (AllowSelectingWhenSelectedByBot)
{
CancelUsing(user);
user = activator;
IsActive = true;
return true;
}
}
return AllowSelectingWhenSelectedByOther;
}
else
{
@@ -10,6 +10,18 @@ namespace Barotrauma.Items.Components
{
private float force;
/// <summary>
/// Latest signal the set_force connection received, used to set <see cref="targetForce"/> in the Update method.
/// We use a separate variable, because otherwise specific item update orders and sending multiple signals to set_force would lead to bugs:
/// targetForce could be set to 0, then a power grid might update as if the engine was off and mark the voltage of the grid as 1,
/// then another item could set the targetForce to 100 and make it run without power.
/// </summary>
private float? lastReceivedTargetForce;
/// <summary>
/// The amount of force the engine is aiming for (the actual force may be less than this,
/// depending on the amount of power, the condition of the engine or boosts from talents)
/// </summary>
private float targetForce;
private float maxForce;
@@ -58,7 +70,7 @@ namespace Barotrauma.Items.Components
public float CurrentVolume
{
get { return Math.Abs((force / 100.0f) * (MinVoltage <= 0.0f ? 1.0f : Math.Min(prevVoltage / MinVoltage, 1.0f))); }
get { return Math.Abs((force / 100.0f) * (MinVoltage <= 0.0f ? 1.0f : Math.Min(prevVoltage, 1.0f))); }
}
public float CurrentBrokenVolume
@@ -110,7 +122,10 @@ namespace Barotrauma.Items.Components
hasPower = Voltage > MinVoltage;
}
if (lastReceivedTargetForce.HasValue)
{
targetForce = lastReceivedTargetForce.Value;
}
Force = MathHelper.Lerp(force, (Voltage < MinVoltage) ? 0.0f : targetForce, deltaTime * 10.0f);
if (Math.Abs(Force) > 1.0f)
{
@@ -254,7 +269,7 @@ namespace Barotrauma.Items.Components
if (float.TryParse(signal.value, NumberStyles.Float, CultureInfo.InvariantCulture, out float tempForce))
{
controlLockTimer = 0.1f;
targetForce = MathHelper.Clamp(tempForce, -100.0f, 100.0f);
lastReceivedTargetForce = MathHelper.Clamp(tempForce, -100.0f, 100.0f);
User = signal.sender;
}
}
@@ -784,8 +784,10 @@ namespace Barotrauma.Items.Components
}
else
{
float condition1 = MathUtils.IsValid(item1.Condition) ? item1.Condition : 0;
float condition2 = MathUtils.IsValid(item2.Condition) ? item2.Condition : 0;
//prefer items in worse condition
return Math.Sign(item2.Condition - item1.Condition);
return Math.Sign(condition2 - condition1);
}
}
@@ -775,7 +775,6 @@ namespace Barotrauma.Items.Components
if (shutDown)
{
PowerOn = false;
AutoTemp = false;
TargetFissionRate = 0.0f;
TargetTurbineOutput = 0.0f;
unsentChanges = true;
@@ -68,7 +68,6 @@ namespace Barotrauma.Items.Components
public bool UseDirectionalPing => useDirectionalPing;
private bool useDirectionalPing = false;
private Vector2 pingDirection = new Vector2(1.0f, 0.0f);
private bool useMineralScanner;
private bool aiPingCheckPending;
@@ -133,6 +132,9 @@ namespace Barotrauma.Items.Components
}
}
[Serialize(true, IsPropertySaveable.Yes, alwaysUseInstanceValues: true)]
public bool UseMineralScanner { get; set; }
public float Zoom
{
get { return zoom; }
@@ -366,7 +368,7 @@ namespace Barotrauma.Items.Components
bool isActive = msg.ReadBoolean();
bool directionalPing = useDirectionalPing;
float zoomT = zoom, pingDirectionT = 0.0f;
bool mineralScanner = useMineralScanner;
bool mineralScanner = UseMineralScanner;
if (isActive)
{
zoomT = msg.ReadRangedSingle(0.0f, 1.0f, 8);
@@ -391,13 +393,13 @@ namespace Barotrauma.Items.Components
float pingAngle = MathHelper.Lerp(0.0f, MathHelper.TwoPi, pingDirectionT);
pingDirection = new Vector2((float)Math.Cos(pingAngle), (float)Math.Sin(pingAngle));
}
useMineralScanner = mineralScanner;
UseMineralScanner = mineralScanner;
#if CLIENT
zoomSlider.BarScroll = zoomT;
directionalModeSwitch.Selected = useDirectionalPing;
if (mineralScannerSwitch != null)
{
mineralScannerSwitch.Selected = useMineralScanner;
mineralScannerSwitch.Selected = UseMineralScanner;
}
#endif
}
@@ -418,7 +420,7 @@ namespace Barotrauma.Items.Components
float pingAngle = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(pingDirection));
msg.WriteRangedSingle(MathUtils.InverseLerp(0.0f, MathHelper.TwoPi, pingAngle), 0.0f, 1.0f, 8);
}
msg.WriteBoolean(useMineralScanner);
msg.WriteBoolean(UseMineralScanner);
}
}
}
@@ -444,7 +444,7 @@ namespace Barotrauma.Items.Components
if (connectedSubUpdateTimer <= 0.0f)
{
connectedSubs.Clear();
connectedSubs = controlledSub?.GetConnectedSubs();
connectedSubs.AddRange(controlledSub.GetConnectedSubs());
connectedSubUpdateTimer = ConnectedSubUpdateInterval;
}
@@ -535,7 +535,7 @@ namespace Barotrauma.Items.Components
foreach (GraphEdge edge in cell.Edges)
{
if (MathUtils.GetLineIntersection(edge.Point1 + cell.Translation, edge.Point2 + cell.Translation, controlledSub.WorldPosition, cell.Center, out Vector2 intersection))
if (MathUtils.GetLineSegmentIntersection(edge.Point1 + cell.Translation, edge.Point2 + cell.Translation, controlledSub.WorldPosition, cell.Center, out Vector2 intersection))
{
Vector2 diff = controlledSub.WorldPosition - intersection;
//far enough -> ignore
@@ -435,7 +435,7 @@ namespace Barotrauma.Items.Components
{
//other junction boxes don't need to receive the signal in the pass-through signal connections
//because we relay it straight to the connected items without going through the whole chain of junction boxes
if (ic is PowerTransfer && !(ic is RelayComponent)) { continue; }
if (ic is PowerTransfer && ic is not RelayComponent) { continue; }
ic.ReceiveSignal(signal, recipient);
}
@@ -709,7 +709,7 @@ namespace Barotrauma.Items.Components
return hits;
}
public override void Drop(Character dropper)
public override void Drop(Character dropper, bool setTransform = true)
{
Item.ResetWaterDragCoefficient();
if (dropper != null)
@@ -717,7 +717,7 @@ namespace Barotrauma.Items.Components
DisableProjectileCollisions();
Unstick();
}
base.Drop(dropper);
base.Drop(dropper, setTransform);
}
public override void Update(float deltaTime, Camera cam)
@@ -939,7 +939,7 @@ namespace Barotrauma.Items.Components
Character character = null;
if (target.Body.UserData is Submarine submarine && target.UserData is not Barotrauma.Item)
{
item.Move(-submarine.Position);
item.Move(-submarine.Position, ignoreContacts: false);
item.Submarine = submarine;
item.body.Submarine = submarine;
return !Hitscan;
@@ -339,18 +339,19 @@ namespace Barotrauma.Items.Components
if (Math.Abs(TargetPullForce) > 0.001f)
{
var targetBody = GetBodyToPull(target);
if (user != null && targetCharacter != null && !user.AnimController.InWater)
bool lerpForces = LerpForces;
if (!lerpForces && user != null && targetCharacter != null && !user.AnimController.InWater)
{
// Prevents rubberbanding horizontally when dragging a corpse.
if ((forceDir.X < 0) != (user.AnimController.Dir < 0))
{
forceDir.X = Math.Clamp(forceDir.X, -0.1f, 0.1f);
// Prevents rubberbanding horizontally when dragging a corpse.
lerpForces = true;
}
}
float force = LerpForces ? MathHelper.Lerp(0, TargetPullForce, MathUtils.InverseLerp(0, MaxLength / 3, distance - 50)) : TargetPullForce;
float force = lerpForces ? MathHelper.Lerp(0, TargetPullForce, MathUtils.InverseLerp(0, MaxLength / 3, distance - 50)) : TargetPullForce;
targetBody?.ApplyForce(-forceDir * force);
var targetRagdoll = targetCharacter?.AnimController;
if (targetRagdoll != null && (targetRagdoll.InWater || targetRagdoll.OnGround))
if (targetRagdoll?.Collider != null && (targetRagdoll.InWater || targetRagdoll.OnGround))
{
targetRagdoll.Collider.ApplyForce(-forceDir * force * 3);
}
@@ -29,10 +29,10 @@ namespace Barotrauma.Items.Components
private readonly Item item;
public readonly bool IsOutput;
public readonly List<StatusEffect> Effects;
public readonly List<ushort> LoadedWireIds;
public readonly List<(ushort wireId, int? connectionIndex)> LoadedWires;
//The grid the connection is a part of
public GridInfo Grid;
@@ -40,6 +40,9 @@ namespace Barotrauma.Items.Components
//Priority in which power output will be handled - load is unaffected
public PowerPriority Priority = PowerPriority.Default;
public Signal LastSentSignal { get; private set; }
public Signal LastReceivedSignal {get; private set;}
public bool IsPower
{
get;
@@ -151,16 +154,20 @@ namespace Barotrauma.Items.Components
IsPower = Name == "power_in" || Name == "power" || Name == "power_out";
LoadedWireIds = new List<ushort>();
LoadedWires = new List<(ushort wireId, int? connectionIndex)>();
foreach (var subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "link":
int id = subElement.GetAttributeInt("w", 0);
int? i = null;
if (subElement.GetAttribute("i") != null)
{
i = subElement.GetAttributeInt("i", 0);
}
if (id < 0) { id = 0; }
if (LoadedWireIds.Count < MaxWires) { LoadedWireIds.Add(idRemap.GetOffsetId(id)); }
if (LoadedWires.Count < MaxWires) { LoadedWires.Add((idRemap.GetOffsetId(id), i)); }
break;
case "statuseffect":
Effects ??= new List<StatusEffect>();
@@ -288,6 +295,7 @@ namespace Barotrauma.Items.Components
public void SendSignal(Signal signal)
{
LastSentSignal = signal;
enumeratingWires = true;
foreach (var wire in wires)
{
@@ -298,6 +306,10 @@ namespace Barotrauma.Items.Components
signal.source?.LastSentSignalRecipients.Add(recipient);
Connection connection = recipient;
connection.LastReceivedSignal = signal;
#if CLIENT
wire.RegisterSignal(signal, source: this);
#endif
object[] obj = new object[] { signal, connection };
GameMain.LuaCs.Hook.Call("signalReceived", obj);
@@ -355,22 +367,29 @@ namespace Barotrauma.Items.Components
public void InitializeFromLoaded()
{
if (LoadedWireIds.Count == 0) { return; }
if (LoadedWires.Count == 0) { return; }
for (int i = 0; i < LoadedWireIds.Count; i++)
foreach ((ushort wireId, int? connectionIndex) in LoadedWires)
{
if (!(Entity.FindEntityByID(LoadedWireIds[i]) is Item wireItem)) { continue; }
if (Entity.FindEntityByID(wireId) is not Item wireItem) { continue; }
var wire = wireItem.GetComponent<Wire>();
if (wire != null && TryAddLink(wire))
{
if (wire.Item.body != null) wire.Item.body.Enabled = false;
wire.Connect(this, false, false);
if (wire.Item.body != null) { wire.Item.body.Enabled = false; }
if (connectionIndex.HasValue)
{
wire.Connect(this, connectionIndex.Value, addNode: false, sendNetworkEvent: false);
}
else
{
wire.TryConnect(this, addNode: false, sendNetworkEvent: false);
}
wire.FixNodeEnds();
recipientsDirty = true;
}
}
LoadedWireIds.Clear();
LoadedWires.Clear();
}
@@ -381,7 +400,8 @@ namespace Barotrauma.Items.Components
foreach (var wire in wires.OrderBy(w => w.Item.ID))
{
newElement.Add(new XElement("link",
new XAttribute("w", wire.Item.ID.ToString())));
new XAttribute("w", wire.Item.ID.ToString()),
new XAttribute("i", wire.Connections[0] == this ? 0 : 1)));
}
parentElement.Add(newElement);
@@ -295,8 +295,8 @@ namespace Barotrauma.Items.Components
for (int i = 0; i < loadedConnections.Count && i < Connections.Count; i++)
{
Connections[i].LoadedWireIds.Clear();
Connections[i].LoadedWireIds.AddRange(loadedConnections[i].LoadedWireIds);
Connections[i].LoadedWires.Clear();
Connections[i].LoadedWires.AddRange(loadedConnections[i].LoadedWires);
}
disconnectedWireIds = element.GetAttributeUshortArray("disconnectedwires", Array.Empty<ushort>()).ToList();
@@ -82,8 +82,10 @@ namespace Barotrauma.Items.Components
signalOut.SendDuration -= 1;
item.SendSignal(new Signal(signalOut.Signal.value, sender: signalOut.Signal.sender, strength: signalOut.Signal.strength), "signal_out");
if (signalOut.SendDuration <= 0)
{
signalQueue.Dequeue();
{
//check the queue isn't empty again, because sending the signal may empty it
//if this component is set to reset when it receives a signal and the signal is routed back to this component
signalQueue.TryDequeue(out _);
}
else
{
@@ -173,6 +173,8 @@ namespace Barotrauma.Items.Components
set
{
lightColor = value;
//reset previously received signal to force updating the color if we receive a set_color signal after the color has been modified manually
prevColorSignal = string.Empty;
#if CLIENT
if (Light != null)
{
@@ -249,6 +251,11 @@ namespace Barotrauma.Items.Components
base.OnItemLoaded();
SetLightSourceState(IsActive, lightBrightness);
turret = item.GetComponent<Turret>();
if (item.body != null)
{
item.body.FarseerBody.OnEnabled += CheckIfNeedsUpdate;
item.body.FarseerBody.OnDisabled += CheckIfNeedsUpdate;
}
#if CLIENT
Drawable = AlphaBlend && Light.LightSprite != null;
if (Screen.Selected.IsEditor)
@@ -277,15 +284,24 @@ namespace Barotrauma.Items.Components
return;
}
if (item.body == null && powerConsumption <= 0.0f && Parent == null && turret == null &&
if ((item.body == null || !item.body.Enabled) &&
powerConsumption <= 0.0f && Parent == null && turret == null &&
(statusEffectLists == null || !statusEffectLists.ContainsKey(ActionType.OnActive)) &&
(IsActiveConditionals == null || IsActiveConditionals.Count == 0))
{
lightBrightness = 1.0f;
SetLightSourceState(true, lightBrightness);
if (item.body != null && !item.body.Enabled)
{
lightBrightness = 0.0f;
SetLightSourceState(false, 0.0f);
}
else
{
lightBrightness = 1.0f;
SetLightSourceState(true, lightBrightness);
}
isOn = true;
SetLightSourceTransformProjSpecific();
base.IsActive = false;
isOn = true;
#if CLIENT
Light.ParentSub = item.Submarine;
#endif
@@ -222,10 +222,10 @@ namespace Barotrauma.Items.Components
{
Vector2 e1 = edge.Point1 + cell.Translation;
Vector2 e2 = edge.Point2 + cell.Translation;
if (MathUtils.LinesIntersect(e1, e2, new Vector2(detectRect.X, detectRect.Y), new Vector2(detectRect.Right, detectRect.Y)) ||
MathUtils.LinesIntersect(e1, e2, new Vector2(detectRect.X, detectRect.Bottom), new Vector2(detectRect.Right, detectRect.Bottom)) ||
MathUtils.LinesIntersect(e1, e2, new Vector2(detectRect.X, detectRect.Y), new Vector2(detectRect.X, detectRect.Bottom)) ||
MathUtils.LinesIntersect(e1, e2, new Vector2(detectRect.Right, detectRect.Y), new Vector2(detectRect.Right, detectRect.Bottom)))
if (MathUtils.LineSegmentsIntersect(e1, e2, new Vector2(detectRect.X, detectRect.Y), new Vector2(detectRect.Right, detectRect.Y)) ||
MathUtils.LineSegmentsIntersect(e1, e2, new Vector2(detectRect.X, detectRect.Bottom), new Vector2(detectRect.Right, detectRect.Bottom)) ||
MathUtils.LineSegmentsIntersect(e1, e2, new Vector2(detectRect.X, detectRect.Y), new Vector2(detectRect.X, detectRect.Bottom)) ||
MathUtils.LineSegmentsIntersect(e1, e2, new Vector2(detectRect.Right, detectRect.Y), new Vector2(detectRect.Right, detectRect.Bottom)))
{
MotionDetected = true;
return;
@@ -8,6 +8,9 @@ namespace Barotrauma.Items.Components
public Item source;
public float power;
public float strength;
public readonly double CreationTime;
public double TimeSinceCreated => Timing.TotalTimeUnpaused - CreationTime;
public Signal(string value, int stepsTaken = 0, Character sender = null,
Item source = null, float power = 0.0f, float strength = 1.0f)
@@ -18,6 +21,7 @@ namespace Barotrauma.Items.Components
this.source = source;
this.power = power;
this.strength = strength;
CreationTime = Timing.TotalTimeUnpaused;
}
internal Signal WithStepsTaken(int stepsTaken)
@@ -64,9 +64,7 @@ namespace Barotrauma.Items.Components
set;
}
private bool linkToChat = false;
[ConditionallyEditable(ConditionallyEditable.ConditionType.AllowLinkingWifiToChat)]
[ConditionallyEditable(ConditionallyEditable.ConditionType.AllowLinkingWifiToChat, onlyInEditors: false)]
[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
@@ -19,7 +19,7 @@ namespace Barotrauma.Items.Components
private Vector2 end;
private readonly float angle;
private readonly float length;
public readonly float Length;
public Vector2 Start
{
@@ -36,7 +36,7 @@ namespace Barotrauma.Items.Components
this.end = end;
angle = MathUtils.VectorToAngle(end - start);
length = Vector2.Distance(start, end);
Length = Vector2.Distance(start, end);
}
}
@@ -52,7 +52,7 @@ namespace Barotrauma.Items.Components
private List<Vector2> nodes;
private readonly List<WireSection> sections;
private Connection[] connections;
private readonly Connection[] connections;
private bool canPlaceNode;
private Vector2 newNodePos;
@@ -81,6 +81,8 @@ namespace Barotrauma.Items.Components
get { return connections; }
}
public float Length { get; private set; }
[Serialize(5000.0f, IsPropertySaveable.No, description: "The maximum distance the wire can extend (in pixels).")]
public float MaxLength
{
@@ -162,14 +164,50 @@ namespace Barotrauma.Items.Components
SetConnectedDirty();
}
public bool Connect(Connection newConnection, bool addNode = true, bool sendNetworkEvent = false)
/// <summary>
/// Tries to add the given connection to this wire. Note that this only affects the wire -
/// adding the wire to the connection is done in <see cref="Connection.ConnectWire(Wire)"/>
/// </summary>
public bool TryConnect(Connection newConnection, bool addNode = true, bool sendNetworkEvent = false)
{
if (connections[0] == null)
{
return Connect(newConnection, 0, addNode, sendNetworkEvent);
}
else if (connections[1] == null)
{
return Connect(newConnection, 1, addNode, sendNetworkEvent);
}
return false;
}
/// <summary>
/// Tries to add the given connection to this wire. Note that this only affects the wire -
/// adding the wire to the connection is done in <see cref="Connection.ConnectWire(Wire)"/>
/// </summary>
/// <param name="connectionIndex">Which end of the wire to add the connection to? 0 or 1.
/// Normally doesn't make a difference, but matters if we're copying/loading a wire,
/// in which case the 1st node should be located at the same item as the 1st connection.</param>
/// <returns></returns>
public bool Connect(Connection newConnection, int connectionIndex, bool addNode = true, bool sendNetworkEvent = false)
{
for (int i = 0; i < 2; i++)
{
if (connections[i] == newConnection) { return false; }
}
if (!connections.Any(c => c == null)) { return false; }
if (connectionIndex < 0 || connectionIndex > 1)
{
DebugConsole.ThrowError($"Error while connecting a wire to {newConnection.Item}: {connectionIndex} is not a valid index.");
return false;
}
if (connections[connectionIndex] != null)
{
DebugConsole.ThrowError($"Error while connecting a wire to {newConnection.Item}: a wire is already connected to the index {connectionIndex}.");
return false;
}
for (int i = 0; i < 2; i++)
{
@@ -183,70 +221,12 @@ namespace Barotrauma.Items.Components
newConnection.ConnectionPanel.DisconnectedWires.Remove(this);
for (int i = 0; i < 2; i++)
connections[connectionIndex] = newConnection;
FixNodeEnds();
if (addNode)
{
if (connections[i] != null) { continue; }
connections[i] = newConnection;
FixNodeEnds();
if (!addNode) { break; }
Submarine refSub = newConnection.Item.Submarine;
if (refSub == null)
{
Structure attachTarget = Structure.GetAttachTarget(newConnection.Item.WorldPosition);
if (attachTarget == null && !(newConnection.Item.GetComponent<Holdable>()?.Attached ?? false))
{
connections[i] = null;
continue;
}
refSub = attachTarget?.Submarine;
}
Vector2 nodePos = refSub == null ?
newConnection.Item.Position :
newConnection.Item.Position - refSub.HiddenSubPosition;
if (nodes.Count > 0 && nodes[0] == nodePos) { break; }
if (nodes.Count > 1 && nodes[nodes.Count - 1] == nodePos) { break; }
//make sure we place the node at the correct end of the wire (the end that's closest to the new node pos)
int newNodeIndex = 0;
if (nodes.Count > 1)
{
if (connections[0] != null && connections[0] != newConnection)
{
if (Vector2.DistanceSquared(nodes[0], connections[0].Item.Position - (refSub?.HiddenSubPosition ?? Vector2.Zero)) <
Vector2.DistanceSquared(nodes[nodes.Count - 1], connections[0].Item.Position - (refSub?.HiddenSubPosition ?? Vector2.Zero)))
{
newNodeIndex = nodes.Count;
}
}
else if (connections[1] != null && connections[1] != newConnection)
{
if (Vector2.DistanceSquared(nodes[0], connections[1].Item.Position - (refSub?.HiddenSubPosition ?? Vector2.Zero)) <
Vector2.DistanceSquared(nodes[nodes.Count - 1], connections[1].Item.Position - (refSub?.HiddenSubPosition ?? Vector2.Zero)))
{
newNodeIndex = nodes.Count;
}
}
else if (Vector2.DistanceSquared(nodes[nodes.Count - 1], nodePos) < Vector2.DistanceSquared(nodes[0], nodePos))
{
newNodeIndex = nodes.Count;
}
}
if (newNodeIndex == 0 && nodes.Count > 1)
{
nodes.Insert(0, nodePos);
}
else
{
nodes.Add(nodePos);
}
break;
AddNode(newConnection, connectionIndex);
}
SetConnectedDirty();
@@ -258,7 +238,7 @@ namespace Barotrauma.Items.Components
if (ic == this) { continue; }
ic.Drop(null);
}
if (item.Container != null) { item.Container.RemoveContained(this.item); }
item.Container?.RemoveContained(item);
if (item.body != null) { item.body.Enabled = false; }
IsActive = false;
@@ -286,6 +266,63 @@ namespace Barotrauma.Items.Components
return true;
}
private void AddNode(Connection newConnection, int selectedIndex)
{
Submarine refSub = newConnection.Item.Submarine;
if (refSub == null)
{
Structure attachTarget = Structure.GetAttachTarget(newConnection.Item.WorldPosition);
if (attachTarget == null && !(newConnection.Item.GetComponent<Holdable>()?.Attached ?? false))
{
connections[selectedIndex] = null;
return;
}
refSub = attachTarget?.Submarine;
}
Vector2 nodePos = refSub == null ?
newConnection.Item.Position :
newConnection.Item.Position - refSub.HiddenSubPosition;
if (nodes.Count > 0 && nodes[0] == nodePos) { return; }
if (nodes.Count > 1 && nodes[nodes.Count - 1] == nodePos) { return; }
//make sure we place the node at the correct end of the wire (the end that's closest to the new node pos)
int newNodeIndex = 0;
if (nodes.Count > 1)
{
if (connections[0] != null && connections[0] != newConnection)
{
if (Vector2.DistanceSquared(nodes[0], connections[0].Item.Position - (refSub?.HiddenSubPosition ?? Vector2.Zero)) <
Vector2.DistanceSquared(nodes[nodes.Count - 1], connections[0].Item.Position - (refSub?.HiddenSubPosition ?? Vector2.Zero)))
{
newNodeIndex = nodes.Count;
}
}
else if (connections[1] != null && connections[1] != newConnection)
{
if (Vector2.DistanceSquared(nodes[0], connections[1].Item.Position - (refSub?.HiddenSubPosition ?? Vector2.Zero)) <
Vector2.DistanceSquared(nodes[nodes.Count - 1], connections[1].Item.Position - (refSub?.HiddenSubPosition ?? Vector2.Zero)))
{
newNodeIndex = nodes.Count;
}
}
else if (Vector2.DistanceSquared(nodes[nodes.Count - 1], nodePos) < Vector2.DistanceSquared(nodes[0], nodePos))
{
newNodeIndex = nodes.Count;
}
}
if (newNodeIndex == 0 && nodes.Count > 1)
{
nodes.Insert(0, nodePos);
}
else
{
nodes.Add(nodePos);
}
}
public override void Equip(Character character)
{
if (shouldClearConnections) { ClearConnections(character); }
@@ -298,7 +335,7 @@ namespace Barotrauma.Items.Components
IsActive = false;
}
public override void Drop(Character dropper)
public override void Drop(Character dropper, bool setTransform = true)
{
if (shouldClearConnections) { ClearConnections(dropper); }
IsActive = false;
@@ -528,6 +565,7 @@ namespace Barotrauma.Items.Components
sections.Add(new WireSection(nodes[i], nodes[i + 1]));
}
Drawable = IsActive || sections.Count > 0;
Length = sections.Count > 0 ? sections.Sum(s => s.Length) : 0;
CalculateExtents();
}
@@ -845,8 +845,13 @@ namespace Barotrauma.Items.Components
{
public readonly Item Projectile;
public EventData(Item projectile)
public EventData(Item projectile, Turret turret)
{
System.Diagnostics.Debug.Assert(projectile != null, $"Tried to create Turret {nameof(EventData)} with no projectile.");
GameAnalyticsManager.AddErrorEventOnce(
"Turret.EventData:entitynull"+ turret.Item.Prefab.Identifier,
GameAnalyticsManager.ErrorSeverity.Error,
$"Turret \"{turret.Item.Prefab.Identifier}\" tried to create {nameof(EventData)} with no projectile.");
Projectile = projectile;
}
}
@@ -918,7 +923,7 @@ namespace Barotrauma.Items.Components
projectile.Container?.RemoveContained(projectile);
}
#if SERVER
item.CreateServerEvent(this, new EventData(projectile));
item.CreateServerEvent(this, new EventData(projectile, this));
#endif
ApplyStatusEffects(ActionType.OnUse, 1.0f, user: user);
@@ -1314,7 +1319,9 @@ namespace Barotrauma.Items.Components
}
// Don't aim monsters that are inside any submarine.
if (!enemy.IsHuman && enemy.CurrentHull != null) { continue; }
if (HumanAIController.IsFriendly(character, enemy)) { continue; }
if (HumanAIController.IsFriendly(character, enemy)) { continue; }
// Don't shoot at captured enemies.
if (enemy.LockHands) { continue; }
float dist = Vector2.DistanceSquared(enemy.WorldPosition, item.WorldPosition);
if (dist > closestDistance) { continue; }
if (dist < shootDistance * shootDistance)
@@ -1413,7 +1420,7 @@ namespace Barotrauma.Items.Components
{
// The closest point can't be targeted -> get a point directly in front of the turret
Vector2 barrelDir = new Vector2((float)Math.Cos(rotation), -(float)Math.Sin(rotation));
if (MathUtils.GetLineIntersection(p1, p2, item.WorldPosition, item.WorldPosition + barrelDir * shootDistance, out Vector2 intersection))
if (MathUtils.GetLineSegmentIntersection(p1, p2, item.WorldPosition, item.WorldPosition + barrelDir * shootDistance, out Vector2 intersection))
{
closestPoint = intersection;
if (!CheckTurretAngle(closestPoint)) { continue; }
@@ -1889,22 +1896,27 @@ namespace Barotrauma.Items.Components
{
if (TryExtractEventData(extraData, out EventData eventData))
{
msg.WriteUInt16(eventData.Projectile.ID);
msg.WriteRangedSingle(MathHelper.Clamp(rotation, minRotation, maxRotation), minRotation, maxRotation, 16);
msg.WriteUInt16(eventData.Projectile?.ID ?? Entity.NullEntityID);
msg.WriteRangedSingle(MathHelper.Clamp(wrapAngle(rotation), minRotation, maxRotation), minRotation, maxRotation, 16);
}
else
{
msg.WriteUInt16((ushort)0);
float wrappedTargetRotation = targetRotation;
while (wrappedTargetRotation < minRotation && MathUtils.IsValid(wrappedTargetRotation))
msg.WriteRangedSingle(MathHelper.Clamp(wrapAngle(targetRotation), minRotation, maxRotation), minRotation, maxRotation, 16);
}
float wrapAngle(float angle)
{
float wrappedAngle = angle;
while (wrappedAngle < minRotation && MathUtils.IsValid(wrappedAngle))
{
wrappedTargetRotation += MathHelper.TwoPi;
wrappedAngle += MathHelper.TwoPi;
}
while (wrappedTargetRotation > maxRotation && MathUtils.IsValid(wrappedTargetRotation))
while (wrappedAngle > maxRotation && MathUtils.IsValid(wrappedAngle))
{
wrappedTargetRotation -= MathHelper.TwoPi;
wrappedAngle -= MathHelper.TwoPi;
}
msg.WriteRangedSingle(MathHelper.Clamp(wrappedTargetRotation, minRotation, maxRotation), minRotation, maxRotation, 16);
return wrappedAngle;
}
}
}
@@ -482,11 +482,11 @@ namespace Barotrauma.Items.Components
character.OnWearablesChanged();
}
public override void Drop(Character dropper)
public override void Drop(Character dropper, bool setTransform = true)
{
Character previousPicker = picker;
Unequip(picker);
base.Drop(dropper);
base.Drop(dropper, setTransform);
previousPicker?.OnWearablesChanged();
picker = null;
IsActive = false;