Unstable v0.1300.0.0 (February 19th 2021)
This commit is contained in:
@@ -18,7 +18,7 @@ namespace Barotrauma.Items.Components
|
||||
protected Vector2[] handlePos;
|
||||
private readonly Vector2[] scaledHandlePos;
|
||||
|
||||
private InputType prevPickKey;
|
||||
private readonly InputType prevPickKey;
|
||||
private string prevMsg;
|
||||
private Dictionary<RelatedItem.RelationType, List<RelatedItem>> prevRequiredItems;
|
||||
|
||||
@@ -29,13 +29,22 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private float swingState;
|
||||
|
||||
private Character prevEquipper;
|
||||
|
||||
private bool attachable, attached, attachedByDefault;
|
||||
private Voronoi2.VoronoiCell attachTargetCell;
|
||||
private readonly PhysicsBody body;
|
||||
public PhysicsBody Pusher
|
||||
{
|
||||
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.")]
|
||||
public bool CanPush
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
//the angle in which the Character holds the item
|
||||
protected float holdAngle;
|
||||
@@ -205,6 +214,7 @@ namespace Barotrauma.Items.Components
|
||||
if (other.Body.UserData is Character character)
|
||||
{
|
||||
if (!IsActive) { return false; }
|
||||
if (!CanPush) { return false; }
|
||||
return character != picker;
|
||||
}
|
||||
else
|
||||
@@ -255,6 +265,7 @@ namespace Barotrauma.Items.Components
|
||||
if (Pusher != null) { Pusher.Enabled = false; }
|
||||
if (item.body != null) { item.body.Enabled = true; }
|
||||
IsActive = false;
|
||||
attachTargetCell = null;
|
||||
|
||||
if (picker == null || picker.Removed)
|
||||
{
|
||||
@@ -299,11 +310,9 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
item.SetTransform(picker.SimPosition, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
picker.DeselectItem(item);
|
||||
picker.Inventory.RemoveItem(item);
|
||||
picker = null;
|
||||
}
|
||||
@@ -338,34 +347,33 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
bool alreadyEquipped = character.HasEquippedItem(item);
|
||||
bool canSelect = picker.TrySelectItem(item);
|
||||
|
||||
if (canSelect || picker.HasEquippedItem(item))
|
||||
if (picker.HasEquippedItem(item))
|
||||
{
|
||||
if (!canSelect)
|
||||
{
|
||||
character.DeselectItem(item);
|
||||
}
|
||||
|
||||
item.body.Enabled = true;
|
||||
item.body.PhysEnabled = false;
|
||||
IsActive = true;
|
||||
|
||||
#if SERVER
|
||||
if (!alreadyEquipped) GameServer.Log(GameServer.CharacterLogName(character) + " equipped " + item.Name, ServerLog.MessageType.ItemInteraction);
|
||||
if (picker != prevEquipper) { GameServer.Log(GameServer.CharacterLogName(character) + " equipped " + item.Name, ServerLog.MessageType.ItemInteraction); }
|
||||
#endif
|
||||
prevEquipper = picker;
|
||||
}
|
||||
else
|
||||
{
|
||||
prevEquipper = null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Unequip(Character character)
|
||||
{
|
||||
if (picker == null) return;
|
||||
|
||||
picker.DeselectItem(item);
|
||||
#if SERVER
|
||||
GameServer.Log(GameServer.CharacterLogName(character) + " unequipped " + item.Name, ServerLog.MessageType.ItemInteraction);
|
||||
if (prevEquipper != null)
|
||||
{
|
||||
GameServer.Log(GameServer.CharacterLogName(character) + " unequipped " + item.Name, ServerLog.MessageType.ItemInteraction);
|
||||
}
|
||||
#endif
|
||||
|
||||
prevEquipper = null;
|
||||
if (picker == null) { return; }
|
||||
item.body.PhysEnabled = true;
|
||||
item.body.Enabled = false;
|
||||
IsActive = false;
|
||||
@@ -383,9 +391,9 @@ namespace Barotrauma.Items.Components
|
||||
//can be attached anywhere inside hulls
|
||||
if (item.CurrentHull != null && Submarine.RectContains(item.CurrentHull.WorldRect, attachPos)) { return true; }
|
||||
|
||||
return Structure.GetAttachTarget(attachPos) != null;
|
||||
return Structure.GetAttachTarget(attachPos) != null || GetAttachTargetCell(100.0f) != null;
|
||||
}
|
||||
|
||||
|
||||
public bool CanBeDeattached()
|
||||
{
|
||||
if (!attachable || !attached) { return true; }
|
||||
@@ -399,14 +407,14 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
//if the item has a connection panel and rewiring is disabled, don't allow deattaching
|
||||
var connectionPanel = item.GetComponent<ConnectionPanel>();
|
||||
if (connectionPanel != null && (connectionPanel.Locked || !(GameMain.NetworkMember?.ServerSettings?.AllowRewiring ?? true)))
|
||||
if (connectionPanel != null && !connectionPanel.AlwaysAllowRewiring && (connectionPanel.Locked || !(GameMain.NetworkMember?.ServerSettings?.AllowRewiring ?? true)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (item.CurrentHull == null)
|
||||
{
|
||||
return Structure.GetAttachTarget(item.WorldPosition) != null;
|
||||
return attachTargetCell != null && Structure.GetAttachTarget(item.WorldPosition) != null;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -464,7 +472,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void AttachToWall()
|
||||
{
|
||||
if (!attachable) return;
|
||||
if (!attachable) { return; }
|
||||
|
||||
//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)
|
||||
@@ -479,15 +487,19 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
item.Submarine = attachTarget.Submarine;
|
||||
}
|
||||
else
|
||||
{
|
||||
attachTargetCell = GetAttachTargetCell(150.0f);
|
||||
if (attachTargetCell != null) { IsActive = true; }
|
||||
}
|
||||
}
|
||||
|
||||
var containedItems = item.OwnInventory?.Items;
|
||||
var containedItems = item.OwnInventory?.AllItems;
|
||||
if (containedItems != null)
|
||||
{
|
||||
foreach (Item contained in containedItems)
|
||||
{
|
||||
if (contained == null) { continue; }
|
||||
if (contained.body == null) { continue; }
|
||||
if (contained?.body == null) { continue; }
|
||||
contained.SetTransform(item.SimPosition, contained.body.Rotation);
|
||||
}
|
||||
}
|
||||
@@ -507,6 +519,7 @@ namespace Barotrauma.Items.Components
|
||||
if (!attachable) return;
|
||||
|
||||
Attached = false;
|
||||
attachTargetCell = null;
|
||||
|
||||
//make the item pickable with the default pick key and with no specific tools/items when it's deattached
|
||||
requiredItems.Clear();
|
||||
@@ -568,9 +581,48 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
Vector2 userPos = useWorldCoordinates ? user.WorldPosition : user.Position;
|
||||
|
||||
return new Vector2(
|
||||
MathUtils.RoundTowardsClosest(userPos.X + mouseDiff.X, Submarine.GridSize.X),
|
||||
MathUtils.RoundTowardsClosest(userPos.Y + mouseDiff.Y, Submarine.GridSize.Y));
|
||||
Vector2 attachPos = userPos + mouseDiff;
|
||||
|
||||
if (user.Submarine == null && Level.Loaded != null)
|
||||
{
|
||||
bool edgeFound = false;
|
||||
foreach (var cell in Level.Loaded.GetCells(attachPos))
|
||||
{
|
||||
if (cell.CellType != Voronoi2.CellType.Solid) { continue; }
|
||||
foreach (var edge in cell.Edges)
|
||||
{
|
||||
if (!edge.IsSolid) { continue; }
|
||||
if (MathUtils.GetLineIntersection(edge.Point1, edge.Point2, user.WorldPosition, attachPos, out Vector2 intersection))
|
||||
{
|
||||
attachPos = intersection;
|
||||
edgeFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (edgeFound) { break; }
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
new Vector2(
|
||||
MathUtils.RoundTowardsClosest(attachPos.X, Submarine.GridSize.X),
|
||||
MathUtils.RoundTowardsClosest(attachPos.Y, Submarine.GridSize.Y));
|
||||
}
|
||||
|
||||
private Voronoi2.VoronoiCell GetAttachTargetCell(float maxDist)
|
||||
{
|
||||
if (Level.Loaded == null) { return null; }
|
||||
foreach (var cell in Level.Loaded.GetCells(item.WorldPosition, searchDepth: 1))
|
||||
{
|
||||
if (cell.CellType != Voronoi2.CellType.Solid) { continue; }
|
||||
Vector2 diff = cell.Center - item.WorldPosition;
|
||||
if (diff.LengthSquared() > 0.0001f) { diff = Vector2.Normalize(diff); }
|
||||
if (cell.IsPointInside(item.WorldPosition + diff * maxDist))
|
||||
{
|
||||
return cell;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public override void UpdateBroken(float deltaTime, Camera cam)
|
||||
@@ -580,14 +632,28 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (attachTargetCell != null)
|
||||
{
|
||||
if (attachTargetCell.CellType != Voronoi2.CellType.Solid)
|
||||
{
|
||||
Drop(dropConnectedWires: true, dropper: null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.body == null || !item.body.Enabled) { return; }
|
||||
if (picker == null || !picker.HasEquippedItem(item))
|
||||
{
|
||||
if (Pusher != null) { Pusher.Enabled = false; }
|
||||
IsActive = false;
|
||||
if (attachTargetCell == null) { IsActive = false; }
|
||||
return;
|
||||
}
|
||||
|
||||
if (picker == Character.Controlled && picker.IsKeyDown(InputType.Aim) && CanBeAttached(picker))
|
||||
{
|
||||
Drawable = true;
|
||||
}
|
||||
|
||||
Vector2 swing = Vector2.Zero;
|
||||
if (swingAmount != Vector2.Zero && !picker.IsUnconscious && picker.Stun <= 0.0f)
|
||||
{
|
||||
@@ -612,7 +678,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
item.Submarine = picker.Submarine;
|
||||
|
||||
if (picker.HasSelectedItem(item))
|
||||
if (picker.HeldItems.Contains(item))
|
||||
{
|
||||
scaledHandlePos[0] = handlePos[0] * item.Scale;
|
||||
scaledHandlePos[1] = handlePos[1] * item.Scale;
|
||||
|
||||
@@ -42,13 +42,13 @@ namespace Barotrauma.Items.Components
|
||||
public override void Equip(Character character)
|
||||
{
|
||||
base.Equip(character);
|
||||
character.Info.CheckDisguiseStatus(true, this);
|
||||
character.Info?.CheckDisguiseStatus(true, this);
|
||||
}
|
||||
|
||||
public override void Unequip(Character character)
|
||||
{
|
||||
base.Unequip(character);
|
||||
character.Info.CheckDisguiseStatus(true, this);
|
||||
character.Info?.CheckDisguiseStatus(true, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,6 +58,13 @@ 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.")]
|
||||
public float RandomOffsetFromWall
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public bool Attached
|
||||
{
|
||||
get { return holdable != null && holdable.Attached; }
|
||||
@@ -77,7 +84,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Vector2.DistanceSquared(item.SimPosition, trigger.SimPosition) > 0.01f)
|
||||
if (trigger != null && Vector2.DistanceSquared(item.SimPosition, trigger.SimPosition) > 0.01f)
|
||||
{
|
||||
trigger.SetTransform(item.SimPosition, 0.0f);
|
||||
}
|
||||
|
||||
@@ -50,6 +50,11 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines items that boost the weapon functionality, like battery cell for stun batons.
|
||||
/// </summary>
|
||||
public readonly string[] PreferredContainedItems;
|
||||
|
||||
public MeleeWeapon(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
@@ -61,6 +66,7 @@ namespace Barotrauma.Items.Components
|
||||
item.IsShootable = true;
|
||||
// TODO: should define this in xml if we have melee weapons that don't require aim to use
|
||||
item.RequireAimToUse = true;
|
||||
PreferredContainedItems = element.GetAttributeStringArray("preferredcontaineditems", new string[0], convertToLowerInvariant: true);
|
||||
}
|
||||
|
||||
public override void Equip(Character character)
|
||||
@@ -76,11 +82,9 @@ namespace Barotrauma.Items.Components
|
||||
if (Item.RequireAimToUse && !character.IsKeyDown(InputType.Aim) || hitting) { return false; }
|
||||
|
||||
//don't allow hitting if the character is already hitting with another weapon
|
||||
for (int i = 0; i < 2; i++ )
|
||||
foreach (Item heldItem in character.HeldItems)
|
||||
{
|
||||
if (character.SelectedItems[i] == null || character.SelectedItems[i] == Item) { continue; }
|
||||
|
||||
var otherWeapon = character.SelectedItems[i].GetComponent<MeleeWeapon>();
|
||||
var otherWeapon = heldItem.GetComponent<MeleeWeapon>();
|
||||
if (otherWeapon == null) { continue; }
|
||||
if (otherWeapon.hitting) { return false; }
|
||||
}
|
||||
@@ -143,13 +147,15 @@ namespace Barotrauma.Items.Components
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (!item.body.Enabled) { impactQueue.Clear(); return; }
|
||||
if (!picker.HasSelectedItem(item)) { impactQueue.Clear(); IsActive = false; }
|
||||
if (picker == null && !picker.HeldItems.Contains(item)) { impactQueue.Clear(); IsActive = false; }
|
||||
|
||||
while (impactQueue.Count > 0)
|
||||
{
|
||||
var impact = impactQueue.Dequeue();
|
||||
HandleImpact(impact.Body);
|
||||
}
|
||||
//in case handling the impact does something to the picker
|
||||
if (picker == null) { return; }
|
||||
|
||||
reloadTimer -= deltaTime;
|
||||
if (reloadTimer < 0) { reloadTimer = 0; }
|
||||
@@ -242,12 +248,14 @@ namespace Barotrauma.Items.Components
|
||||
return true;
|
||||
}
|
||||
|
||||
//ignore collision if there's a wall between the user and the weapon to prevent hitting through walls
|
||||
contact.GetWorldManifold(out Vector2 normal, out var points);
|
||||
|
||||
//ignore collision if there's a wall between the user and the contact point to prevent hitting through walls
|
||||
if (Submarine.PickBody(User.AnimController.AimSourceSimPos,
|
||||
item.SimPosition,
|
||||
points[0],
|
||||
collisionCategory: Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionItemBlocking,
|
||||
allowInsideFixture: true,
|
||||
customPredicate: (Fixture fixture) => { return fixture.CollidesWith.HasFlag(Physics.CollisionItem); }) != null)
|
||||
customPredicate: (Fixture fixture) => { return fixture.CollidesWith.HasFlag(Physics.CollisionItem) && fixture.Body != f2.Body; }) != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
@@ -91,7 +92,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (picker.Inventory.TryPutItemWithAutoEquipCheck(item, picker, allowedSlots))
|
||||
{
|
||||
if (!picker.HasSelectedItem(item) && item.body != null) item.body.Enabled = false;
|
||||
if (!picker.HeldItems.Contains(item) && item.body != null) { item.body.Enabled = false; }
|
||||
this.picker = picker;
|
||||
|
||||
for (int i = item.linkedTo.Count - 1; i >= 0; i--)
|
||||
|
||||
@@ -71,11 +71,11 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
character.AnimController.Collider.ApplyForce(propulsion, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
|
||||
|
||||
if (character.SelectedItems[0] == item)
|
||||
if (character.Inventory.IsInLimbSlot(item, InvSlotType.RightHand))
|
||||
{
|
||||
character.AnimController.GetLimb(LimbType.RightHand)?.body.ApplyForce(propulsion, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
|
||||
}
|
||||
if (character.SelectedItems[1] == item)
|
||||
if (character.Inventory.IsInLimbSlot(item, InvSlotType.LeftHand))
|
||||
{
|
||||
character.AnimController.GetLimb(LimbType.LeftHand)?.body.ApplyForce(propulsion, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public Projectile FindProjectile(bool triggerOnUseOnContainers = false)
|
||||
{
|
||||
var containedItems = item.OwnInventory?.Items;
|
||||
var containedItems = item.OwnInventory?.AllItemsMod;
|
||||
if (containedItems == null) { return null; }
|
||||
|
||||
foreach (Item item in containedItems)
|
||||
@@ -166,7 +166,7 @@ namespace Barotrauma.Items.Components
|
||||
foreach (Item it in containedItems)
|
||||
{
|
||||
if (it == null) { continue; }
|
||||
var containedSubItems = it.OwnInventory?.Items;
|
||||
var containedSubItems = it.OwnInventory?.AllItemsMod;
|
||||
if (containedSubItems == null) { continue; }
|
||||
foreach (Item subItem in containedSubItems)
|
||||
{
|
||||
|
||||
+130
-101
@@ -87,6 +87,13 @@ namespace Barotrauma.Items.Components
|
||||
[Serialize(false, false, 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.")]
|
||||
public float MaxOverlappingWallDist
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
[Serialize(true, false, description: "Can the item hit broken doors.")]
|
||||
public bool HitItems { get; set; }
|
||||
|
||||
@@ -109,10 +116,11 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
get
|
||||
{
|
||||
if (item.body == null) { return BarrelPos; }
|
||||
Matrix bodyTransform = Matrix.CreateRotationZ(item.body.Rotation + MathHelper.ToRadians(BarrelRotation));
|
||||
Vector2 flippedPos = BarrelPos;
|
||||
if (item.body.Dir < 0.0f) { flippedPos.X = -flippedPos.X; }
|
||||
return (Vector2.Transform(flippedPos, bodyTransform));
|
||||
return Vector2.Transform(flippedPos, bodyTransform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,11 +236,15 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
float spread = MathHelper.ToRadians(MathHelper.Lerp(UnskilledSpread, Spread, degreeOfSuccess));
|
||||
float angle = item.body.Rotation + MathHelper.ToRadians(BarrelRotation) + spread * Rand.Range(-0.5f, 0.5f);
|
||||
Vector2 rayEnd = rayStartWorld +
|
||||
ConvertUnits.ToSimUnits(new Vector2(
|
||||
(float)Math.Cos(angle),
|
||||
(float)Math.Sin(angle)) * Range * item.body.Dir);
|
||||
|
||||
float angle = MathHelper.ToRadians(BarrelRotation) + spread * Rand.Range(-0.5f, 0.5f);
|
||||
float dir = 1;
|
||||
if (item.body != null)
|
||||
{
|
||||
angle += item.body.Rotation;
|
||||
dir = item.body.Dir;
|
||||
}
|
||||
Vector2 rayEnd = rayStartWorld + ConvertUnits.ToSimUnits(new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle)) * Range * dir);
|
||||
|
||||
ignoredBodies.Clear();
|
||||
if (character != null)
|
||||
@@ -315,9 +327,14 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
var bodies = Submarine.PickBodies(rayStart, rayEnd, ignoredBodies, collisionCategories,
|
||||
ignoreSensors: false,
|
||||
customPredicate: (Fixture f) =>
|
||||
customPredicate: (Fixture f) =>
|
||||
{
|
||||
if (RepairThroughHoles && f.IsSensor && f.Body?.UserData is Structure || (f.Body?.UserData is Item it && it.GetComponent<Planter>() != null)) { return false; }
|
||||
if (f.IsSensor)
|
||||
{
|
||||
if (RepairThroughHoles && f.Body?.UserData is Structure) { return false; }
|
||||
if (f.Body?.UserData is PhysicsBody) { return false; }
|
||||
}
|
||||
if (f.Body?.UserData is Item it && it.GetComponent<Planter>() != null) { return false; }
|
||||
if (f.Body?.UserData as string == "ruinroom") { return false; }
|
||||
if (f.Body?.UserData is VineTile && !(FireDamage > 0)) { return false; }
|
||||
return true;
|
||||
@@ -356,12 +373,13 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
//if repairing through walls is not allowed and the next wall is more than 100 pixels away from the previous one, stop here
|
||||
//(= repairing multiple overlapping walls is allowed as long as the edges of the walls are less than 100 pixels apart)
|
||||
//(= repairing multiple overlapping walls is allowed as long as the edges of the walls are less than MaxOverlappingWallDist pixels apart)
|
||||
float thisBodyFraction = Submarine.LastPickedBodyDist(body);
|
||||
if (!RepairThroughWalls && lastHitType == typeof(Structure) && Range * (thisBodyFraction - lastPickedFraction) > 100.0f)
|
||||
if (!RepairThroughWalls && lastHitType == typeof(Structure) && Range * (thisBodyFraction - lastPickedFraction) > MaxOverlappingWallDist)
|
||||
{
|
||||
break;
|
||||
}
|
||||
pickedPosition = rayStart + (rayEnd - rayStart) * thisBodyFraction;
|
||||
if (FixBody(user, deltaTime, degreeOfSuccess, body))
|
||||
{
|
||||
lastPickedFraction = thisBodyFraction;
|
||||
@@ -371,13 +389,16 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
FixBody(user, deltaTime, degreeOfSuccess,
|
||||
Submarine.PickBody(rayStart, rayEnd,
|
||||
ignoredBodies, collisionCategories,
|
||||
var pickedBody = Submarine.PickBody(rayStart, rayEnd,
|
||||
ignoredBodies, collisionCategories,
|
||||
ignoreSensors: false,
|
||||
customPredicate: (Fixture f) =>
|
||||
customPredicate: (Fixture f) =>
|
||||
{
|
||||
if (RepairThroughHoles && f.IsSensor && f.Body?.UserData is Structure) { return false; }
|
||||
if (f.IsSensor)
|
||||
{
|
||||
if (RepairThroughHoles && f.Body?.UserData is Structure) { return false; }
|
||||
if (f.Body?.UserData is PhysicsBody) { return false; }
|
||||
}
|
||||
if (f.Body?.UserData as string == "ruinroom") { return false; }
|
||||
if (f.Body?.UserData is VineTile && !(FireDamage > 0)) { return false; }
|
||||
|
||||
@@ -393,9 +414,11 @@ namespace Barotrauma.Items.Components
|
||||
if (targetItem.Condition <= 0) { return false; }
|
||||
}
|
||||
}
|
||||
return f.Body?.UserData != null;
|
||||
return f.Body?.UserData != null;
|
||||
},
|
||||
allowInsideFixture: true));
|
||||
allowInsideFixture: true);
|
||||
pickedPosition = Submarine.LastPickedPosition;
|
||||
FixBody(user, deltaTime, degreeOfSuccess, pickedBody);
|
||||
lastPickedFraction = Submarine.LastPickedFraction;
|
||||
}
|
||||
|
||||
@@ -438,7 +461,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
if (WaterAmount > 0.0f && item.CurrentHull?.Submarine != null)
|
||||
if (WaterAmount > 0.0f && item.Submarine != null)
|
||||
{
|
||||
Vector2 pos = ConvertUnits.ToDisplayUnits(rayStart + item.Submarine.SimPosition);
|
||||
|
||||
@@ -466,7 +489,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, GUI.Style.Blue, GUI.Style.Blue, "progressbar.watering");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -490,8 +513,6 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (targetBody?.UserData == null) { return false; }
|
||||
|
||||
pickedPosition = Submarine.LastPickedPosition;
|
||||
|
||||
if (targetBody.UserData is Structure targetStructure)
|
||||
{
|
||||
if (targetStructure.IsPlatform) { return false; }
|
||||
@@ -519,10 +540,9 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (targetBody.UserData is Voronoi2.VoronoiCell cell)
|
||||
else if (targetBody.UserData is Voronoi2.VoronoiCell cell && cell.IsDestructible)
|
||||
{
|
||||
var levelWall = Level.Loaded?.ExtraWalls.Find(w => w.Body == cell.Body) as DestructibleLevelWall;
|
||||
if (levelWall != null)
|
||||
if (Level.Loaded?.ExtraWalls.Find(w => w.Body == cell.Body) is DestructibleLevelWall levelWall)
|
||||
{
|
||||
levelWall.AddDamage(-LevelWallFixAmount * deltaTime, item.WorldPosition);
|
||||
}
|
||||
@@ -574,7 +594,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else if (targetBody.UserData is Item targetItem)
|
||||
{
|
||||
if (!HitItems || targetItem.NonInteractable) { return false; }
|
||||
if (!HitItems || !targetItem.IsInteractable(user)) { return false; }
|
||||
|
||||
var levelResource = targetItem.GetComponent<LevelResource>();
|
||||
if (levelResource != null && levelResource.Attached &&
|
||||
@@ -589,7 +609,7 @@ namespace Barotrauma.Items.Components
|
||||
levelResource.DeattachTimer / levelResource.DeattachDuration,
|
||||
GUI.Style.Red, GUI.Style.Green, "progressbar.deattaching");
|
||||
#endif
|
||||
FixItemProjSpecific(user, deltaTime, targetItem);
|
||||
FixItemProjSpecific(user, deltaTime, targetItem, showProgressBar: false);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -615,7 +635,7 @@ namespace Barotrauma.Items.Components
|
||||
targetItem.body.ApplyForce(dir * TargetForce, maxVelocity: 10.0f);
|
||||
}
|
||||
|
||||
FixItemProjSpecific(user, deltaTime, targetItem);
|
||||
FixItemProjSpecific(user, deltaTime, targetItem, showProgressBar: true);
|
||||
return true;
|
||||
}
|
||||
else if (targetBody.UserData is BallastFloraBranch branch)
|
||||
@@ -630,7 +650,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
partial void FixStructureProjSpecific(Character user, float deltaTime, Structure targetStructure, int sectionIndex);
|
||||
partial void FixCharacterProjSpecific(Character user, float deltaTime, Character targetCharacter);
|
||||
partial void FixItemProjSpecific(Character user, float deltaTime, Item targetItem);
|
||||
partial void FixItemProjSpecific(Character user, float deltaTime, Item targetItem, bool showProgressBar);
|
||||
|
||||
private float sinTime;
|
||||
private float repairTimer;
|
||||
@@ -638,74 +658,71 @@ namespace Barotrauma.Items.Components
|
||||
private readonly float repairTimeOut = 5;
|
||||
public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
|
||||
{
|
||||
if (!(objective.OperateTarget is Gap leak)) { return true; }
|
||||
if (leak.Submarine == null) { return true; }
|
||||
if (!(objective.OperateTarget is Gap leak))
|
||||
{
|
||||
Reset();
|
||||
return true;
|
||||
}
|
||||
if (leak.Submarine == null)
|
||||
{
|
||||
Reset();
|
||||
return true;
|
||||
}
|
||||
if (leak != previousGap)
|
||||
{
|
||||
sinTime = 0;
|
||||
repairTimer = 0;
|
||||
Reset();
|
||||
previousGap = leak;
|
||||
}
|
||||
Vector2 fromCharacterToLeak = leak.WorldPosition - character.WorldPosition;
|
||||
float dist = fromCharacterToLeak.Length();
|
||||
float reach = AIObjectiveFixLeak.CalculateReach(this, character);
|
||||
|
||||
//too far away -> consider this done and hope the AI is smart enough to move closer
|
||||
if (dist > reach * 2) { return true; }
|
||||
character.AIController.SteeringManager.Reset();
|
||||
//steer closer if almost in range
|
||||
if (dist > reach)
|
||||
if (dist > reach * 3)
|
||||
{
|
||||
if (character.AnimController.InWater)
|
||||
// Too far away -> consider this done and hope the AI is smart enough to move closer
|
||||
Reset();
|
||||
return true;
|
||||
}
|
||||
character.AIController.SteeringManager.Reset();
|
||||
if (!character.AnimController.InWater)
|
||||
{
|
||||
// TODO: use the collider size?
|
||||
if (!character.AnimController.InWater && character.AnimController is HumanoidAnimController &&
|
||||
Math.Abs(fromCharacterToLeak.X) < 100.0f && fromCharacterToLeak.Y < 0.0f && fromCharacterToLeak.Y > -150.0f)
|
||||
{
|
||||
if (character.AIController.SteeringManager is IndoorsSteeringManager indoorSteering)
|
||||
((HumanoidAnimController)character.AnimController).Crouching = true;
|
||||
}
|
||||
}
|
||||
if (dist > reach * 0.8f || dist > reach * 0.5f && character.AnimController.Limbs.Any(l => l.inWater))
|
||||
{
|
||||
// Steer closer
|
||||
if (character.AIController.SteeringManager is IndoorsSteeringManager indoorSteering)
|
||||
{
|
||||
// Swimming inside the sub
|
||||
if (indoorSteering.CurrentPath != null && !indoorSteering.IsPathDirty && (indoorSteering.CurrentPath.Unreachable || indoorSteering.CurrentPath.Finished))
|
||||
{
|
||||
// Swimming inside the sub
|
||||
if (indoorSteering.CurrentPath != null && !indoorSteering.IsPathDirty && indoorSteering.CurrentPath.Unreachable)
|
||||
{
|
||||
Vector2 dir = Vector2.Normalize(fromCharacterToLeak);
|
||||
character.AIController.SteeringManager.SteeringManual(deltaTime, dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
character.AIController.SteeringManager.SteeringSeek(character.GetRelativeSimPosition(leak));
|
||||
}
|
||||
Vector2 dir = Vector2.Normalize(fromCharacterToLeak);
|
||||
character.AIController.SteeringManager.SteeringManual(deltaTime, dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Swimming outside the sub
|
||||
character.AIController.SteeringManager.SteeringSeek(character.GetRelativeSimPosition(leak));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: use the collider size?
|
||||
if (!character.AnimController.InWater && character.AnimController is HumanoidAnimController &&
|
||||
Math.Abs(fromCharacterToLeak.X) < 100.0f && fromCharacterToLeak.Y < 0.0f && fromCharacterToLeak.Y > -150.0f)
|
||||
{
|
||||
((HumanoidAnimController)character.AnimController).Crouching = true;
|
||||
}
|
||||
Vector2 standPos = new Vector2(Math.Sign(-fromCharacterToLeak.X), Math.Sign(-fromCharacterToLeak.Y)) / 2;
|
||||
if (leak.IsHorizontal)
|
||||
{
|
||||
standPos.X *= 2;
|
||||
standPos.Y = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
standPos.X = 0;
|
||||
}
|
||||
character.AIController.SteeringManager.SteeringSeek(standPos);
|
||||
// Swimming outside the sub
|
||||
character.AIController.SteeringManager.SteeringSeek(character.GetRelativeSimPosition(leak));
|
||||
}
|
||||
}
|
||||
if (dist < reach / 2)
|
||||
else if (dist < reach * 0.25f)
|
||||
{
|
||||
// Too close -> steer away
|
||||
character.AIController.SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(character.SimPosition - leak.SimPosition));
|
||||
}
|
||||
else if (dist < reach * 2)
|
||||
if (dist <= reach)
|
||||
{
|
||||
// In or almost in range
|
||||
// In range
|
||||
character.CursorPosition = leak.WorldPosition;
|
||||
if (character.Submarine != null)
|
||||
{
|
||||
@@ -729,51 +746,57 @@ namespace Barotrauma.Items.Components
|
||||
character.AIController.SteeringManager.SteeringManual(deltaTime, moveDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (item.RequireAimToUse)
|
||||
{
|
||||
character.SetInput(InputType.Aim, false, true);
|
||||
sinTime += deltaTime * 5;
|
||||
}
|
||||
// Press the trigger only when the tool is approximately facing the target.
|
||||
Vector2 fromItemToLeak = leak.WorldPosition - item.WorldPosition;
|
||||
var angle = VectorExtensions.Angle(VectorExtensions.Forward(item.body.TransformedRotation), fromItemToLeak);
|
||||
if (angle < MathHelper.PiOver4)
|
||||
{
|
||||
if (Submarine.PickBody(item.SimPosition, leak.SimPosition, collisionCategory: Physics.CollisionWall, allowInsideFixture: true)?.UserData is Item i)
|
||||
if (item.RequireAimToUse)
|
||||
{
|
||||
var door = i.GetComponent<Door>();
|
||||
// Hit a door, abandon so that we don't weld it shut.
|
||||
return door != null && !door.IsOpen && !door.IsBroken;
|
||||
character.SetInput(InputType.Aim, false, true);
|
||||
sinTime += deltaTime * 5;
|
||||
}
|
||||
// Check that we don't hit any friendlies
|
||||
if (Submarine.PickBodies(item.SimPosition, leak.SimPosition, collisionCategory: Physics.CollisionCharacter).None(hit =>
|
||||
// Press the trigger only when the tool is approximately facing the target.
|
||||
Vector2 fromItemToLeak = leak.WorldPosition - item.WorldPosition;
|
||||
var angle = VectorExtensions.Angle(VectorExtensions.Forward(item.body.TransformedRotation), fromItemToLeak);
|
||||
if (angle < MathHelper.PiOver4)
|
||||
{
|
||||
if (hit.UserData is Character c)
|
||||
if (Submarine.PickBody(item.SimPosition, leak.SimPosition, collisionCategory: Physics.CollisionWall, allowInsideFixture: true)?.UserData is Item i)
|
||||
{
|
||||
if (c == character) { return false; }
|
||||
return HumanAIController.IsFriendly(character, c);
|
||||
var door = i.GetComponent<Door>();
|
||||
// Hit a door, abandon so that we don't weld it shut.
|
||||
return door != null && !door.CanBeTraversed;
|
||||
}
|
||||
return false;
|
||||
}))
|
||||
{
|
||||
character.SetInput(InputType.Shoot, false, true);
|
||||
Use(deltaTime, character);
|
||||
repairTimer += deltaTime;
|
||||
if (repairTimer > repairTimeOut)
|
||||
// Check that we don't hit any friendlies
|
||||
if (Submarine.PickBodies(item.SimPosition, leak.SimPosition, collisionCategory: Physics.CollisionCharacter).None(hit =>
|
||||
{
|
||||
if (hit.UserData is Character c)
|
||||
{
|
||||
if (c == character) { return false; }
|
||||
return HumanAIController.IsFriendly(character, c);
|
||||
}
|
||||
return false;
|
||||
}))
|
||||
{
|
||||
character.SetInput(InputType.Shoot, false, true);
|
||||
Use(deltaTime, character);
|
||||
repairTimer += deltaTime;
|
||||
if (repairTimer > repairTimeOut)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.NewMessage($"{character.Name}: timed out while welding a leak in {leak.FlowTargetHull.DisplayName}.", color: Color.Yellow);
|
||||
DebugConsole.NewMessage($"{character.Name}: timed out while welding a leak in {leak.FlowTargetHull.DisplayName}.", color: Color.Yellow);
|
||||
#endif
|
||||
return true;
|
||||
Reset();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reset the timer so that we don't time out if the water forces push us away
|
||||
repairTimer = 0;
|
||||
}
|
||||
|
||||
bool leakFixed = (leak.Open <= 0.0f || leak.Removed) &&
|
||||
(leak.ConnectedWall == null || leak.ConnectedWall.Sections.Average(s => s.damage) < 1);
|
||||
|
||||
if (leakFixed && leak.FlowTargetHull?.DisplayName != null)
|
||||
if (leakFixed && leak.FlowTargetHull?.DisplayName != null && character.IsOnPlayerTeam)
|
||||
{
|
||||
if (!leak.FlowTargetHull.ConnectedGaps.Any(g => !g.IsRoomToRoom && g.Open > 0.0f))
|
||||
{
|
||||
@@ -786,6 +809,12 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
return leakFixed;
|
||||
|
||||
void Reset()
|
||||
{
|
||||
sinTime = 0;
|
||||
repairTimer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyStatusEffectsOnTarget(Character user, float deltaTime, ActionType actionType, IEnumerable<ISerializableEntity> targets)
|
||||
@@ -816,7 +845,7 @@ namespace Barotrauma.Items.Components
|
||||
foreach (ISerializableEntity target in targets)
|
||||
{
|
||||
if (!(target is Door door)) { continue; }
|
||||
if (!door.CanBeWelded || door.Item.NonInteractable) { continue; }
|
||||
if (!door.CanBeWelded || !door.Item.IsInteractable(user)) { continue; }
|
||||
for (int i = 0; i < effect.propertyNames.Length; i++)
|
||||
{
|
||||
string propertyName = effect.propertyNames[i];
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
@@ -83,7 +84,7 @@ namespace Barotrauma.Items.Components
|
||||
return;
|
||||
}
|
||||
|
||||
if (picker == null || picker.Removed || !picker.HasSelectedItem(item))
|
||||
if (picker == null || picker.Removed || !picker.HeldItems.Contains(item))
|
||||
{
|
||||
IsActive = false;
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user