v0.11.0.9
This commit is contained in:
@@ -89,6 +89,16 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Automatically cleared after docking -> no need to unregister
|
||||
/// </summary>
|
||||
public event Action OnDocked;
|
||||
|
||||
/// <summary>
|
||||
/// Automatically cleared after undocking -> no need to unregister
|
||||
/// </summary>
|
||||
public event Action OnUnDocked;
|
||||
|
||||
public DockingPort(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
@@ -213,6 +223,9 @@ namespace Barotrauma.Items.Components
|
||||
item.CreateServerEvent(this);
|
||||
}
|
||||
#endif
|
||||
|
||||
OnDocked?.Invoke();
|
||||
OnDocked = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -817,6 +830,9 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
docked = false;
|
||||
|
||||
Item.Submarine.EnableObstructedWaypoints(DockingTarget.Item.Submarine);
|
||||
obstructedWayPointsDisabled = false;
|
||||
|
||||
DockingTarget.Undock();
|
||||
DockingTarget = null;
|
||||
|
||||
@@ -860,9 +876,6 @@ namespace Barotrauma.Items.Components
|
||||
outsideBlocker?.Body.Remove(outsideBlocker);
|
||||
outsideBlocker = null;
|
||||
|
||||
Item.Submarine.EnableObstructedWaypoints();
|
||||
obstructedWayPointsDisabled = false;
|
||||
|
||||
#if SERVER
|
||||
if (GameMain.Server != null && (!item.Submarine?.Loading ?? true))
|
||||
{
|
||||
@@ -870,6 +883,8 @@ namespace Barotrauma.Items.Components
|
||||
item.CreateServerEvent(this);
|
||||
}
|
||||
#endif
|
||||
OnUnDocked?.Invoke();
|
||||
OnUnDocked = null;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
@@ -1034,7 +1049,6 @@ namespace Barotrauma.Items.Components
|
||||
Dock(dockingPort);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0.0f, float signalStrength = 1.0f)
|
||||
|
||||
@@ -23,6 +23,20 @@ namespace Barotrauma.Items.Components
|
||||
private readonly Sprite doorSprite, weldedSprite, brokenSprite;
|
||||
private readonly bool scaleBrokenSprite, fadeBrokenSprite;
|
||||
private readonly bool autoOrientGap;
|
||||
|
||||
private bool isJammed;
|
||||
public bool IsJammed
|
||||
{
|
||||
get { return isJammed; }
|
||||
set
|
||||
{
|
||||
if (isJammed == value) { return; }
|
||||
isJammed = value;
|
||||
#if SERVER
|
||||
item.CreateServerEvent(this);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private bool isStuck;
|
||||
public bool IsStuck
|
||||
@@ -297,7 +311,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (toggleCooldownTimer > 0.0f && user != lastUser) { OnFailedToOpen(); return; }
|
||||
toggleCooldownTimer = ToggleCoolDown;
|
||||
if (IsStuck) { toggleCooldownTimer = 1.0f; OnFailedToOpen(); return; }
|
||||
if (IsStuck || IsJammed) { toggleCooldownTimer = 1.0f; OnFailedToOpen(); return; }
|
||||
lastUser = user;
|
||||
SetState(PredictedState == null ? !isOpen : !PredictedState.Value, false, true, forcedOpen: actionType == ActionType.OnPicked);
|
||||
}
|
||||
@@ -341,7 +355,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
bool isClosing = false;
|
||||
if (!IsStuck)
|
||||
if ((!IsStuck && !IsJammed) || !isOpen)
|
||||
{
|
||||
if (PredictedState == null)
|
||||
{
|
||||
@@ -630,7 +644,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0.0f, float signalStrength = 1.0f)
|
||||
{
|
||||
if (IsStuck) { return; }
|
||||
if (IsStuck || IsJammed) { return; }
|
||||
|
||||
bool wasOpen = PredictedState == null ? isOpen : PredictedState.Value;
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(0.25f, true, description: "The duration of an individual discharge (in seconds)."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f)]
|
||||
[Serialize(0.25f, true, description: "The duration of an individual discharge (in seconds)."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 60.0f, ValueStep = 0.1f, DecimalCount = 2)]
|
||||
public float Duration
|
||||
{
|
||||
get;
|
||||
@@ -193,7 +193,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
partial void DischargeProjSpecific();
|
||||
|
||||
private void FindNodes(Vector2 worldPosition, float range)
|
||||
public void FindNodes(Vector2 worldPosition, float range)
|
||||
{
|
||||
//see which submarines are within range so we can skip structures that are in far-away subs
|
||||
List<Submarine> submarinesInRange = new List<Submarine>();
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
@@ -10,6 +9,7 @@ using FarseerPhysics;
|
||||
using FarseerPhysics.Dynamics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Vector2 = Microsoft.Xna.Framework.Vector2;
|
||||
using Vector4 = Microsoft.Xna.Framework.Vector4;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -138,19 +138,25 @@ namespace Barotrauma.Items.Components
|
||||
public TileSide Sides = TileSide.None;
|
||||
public TileSide BlockedSides = TileSide.None;
|
||||
|
||||
public readonly FoliageConfig FlowerConfig;
|
||||
public readonly FoliageConfig LeafConfig;
|
||||
public FoliageConfig FlowerConfig;
|
||||
public FoliageConfig LeafConfig;
|
||||
|
||||
public int FailedGrowthAttempts;
|
||||
public Rectangle Rect;
|
||||
public Vector2 Position;
|
||||
public Color HealthColor = Color.Transparent;
|
||||
public float DecayDelay;
|
||||
|
||||
private float VineStep;
|
||||
private float FlowerStep;
|
||||
private readonly float diameter;
|
||||
public Vector2 offset;
|
||||
|
||||
public VineTileType Type;
|
||||
public readonly Dictionary<TileSide, Vector2> AdjacentPositions;
|
||||
public static int Size = 32;
|
||||
|
||||
|
||||
public float VineStep;
|
||||
public float FlowerStep;
|
||||
|
||||
private float growthStep;
|
||||
|
||||
public float GrowthStep
|
||||
{
|
||||
get => growthStep;
|
||||
@@ -166,17 +172,12 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
private readonly float diameter;
|
||||
private Vector2 offset;
|
||||
public Color HealthColor = Color.Transparent;
|
||||
public float DecayDelay;
|
||||
|
||||
private readonly Growable Parent;
|
||||
public VineTileType Type;
|
||||
private readonly Growable? Parent;
|
||||
|
||||
public readonly Dictionary<TileSide, Vector2> AdjacentPositions;
|
||||
|
||||
public static int Size = 32;
|
||||
|
||||
public VineTile(Growable parent, Vector2 position, VineTileType type, FoliageConfig? flowerConfig = null, FoliageConfig? leafConfig = null, Rectangle? rect = null)
|
||||
public VineTile(Growable? parent, Vector2 position, VineTileType type, FoliageConfig? flowerConfig = null, FoliageConfig? leafConfig = null, Rectangle? rect = null)
|
||||
{
|
||||
FlowerConfig = flowerConfig ?? FoliageConfig.EmptyConfig;
|
||||
LeafConfig = leafConfig ?? FoliageConfig.EmptyConfig;
|
||||
@@ -197,7 +198,9 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void UpdateScale(float deltaTime)
|
||||
{
|
||||
if (Parent.Decayed && GrowthStep > 1.0f)
|
||||
bool decayed = Parent?.Decayed ?? false;
|
||||
|
||||
if (decayed && GrowthStep > 1.0f)
|
||||
{
|
||||
if (DecayDelay > 0)
|
||||
{
|
||||
@@ -209,7 +212,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
if (GrowthStep >= 2.0f || Parent.Decayed) { return; }
|
||||
if (GrowthStep >= 2.0f || decayed) { return; }
|
||||
|
||||
GrowthStep += deltaTime;
|
||||
|
||||
@@ -282,13 +285,26 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
int value = pool[Growable.RandomInt(0, possible, random)];
|
||||
int value;
|
||||
if (Parent == null)
|
||||
{
|
||||
value = pool[Growable.RandomInt(0, possible, random)];
|
||||
}
|
||||
else
|
||||
{
|
||||
var (x, y, z, w) = Parent.GrowthWeights;
|
||||
float[] weights = { x, y, z, w };
|
||||
|
||||
value = pool.RandomElementByWeight(i => weights[i]);
|
||||
}
|
||||
|
||||
return (TileSide) (1 << value);
|
||||
}
|
||||
|
||||
public bool CanGrowMore() => (Sides | BlockedSides).Count() < 4;
|
||||
|
||||
public bool IsSideBlocked(TileSide side) => BlockedSides.IsBitSet(side) || Sides.IsBitSet(side);
|
||||
|
||||
public static Rectangle CreatePlantRect(Vector2 pos) => new Rectangle((int) pos.X - Size / 2, (int) pos.Y + Size / 2, Size, Size);
|
||||
}
|
||||
|
||||
@@ -313,6 +329,11 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public static TileSide GetOppositeSide(this TileSide side)
|
||||
{
|
||||
return (TileSide) (1 << ((int) Math.Log2((int) side) + 2) % 4);
|
||||
}
|
||||
}
|
||||
|
||||
internal partial class Growable : ItemComponent, IServerSerializable
|
||||
@@ -371,6 +392,9 @@ namespace Barotrauma.Items.Components
|
||||
[Serialize("0.26,0.27,0.29,1.0", true, "Tint of a dead plant.")]
|
||||
public Color DeadTint { get; set; }
|
||||
|
||||
[Serialize("1,1,1,1", true, "Probability for the plant to grow in a direction.")]
|
||||
public Vector4 GrowthWeights { get; set; }
|
||||
|
||||
private const float increasedDeathSpeed = 10f;
|
||||
private bool accelerateDeath;
|
||||
private float health;
|
||||
@@ -666,7 +690,23 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
TileSide side = oldVines.GetRandomFreeSide(random);
|
||||
|
||||
if (side == TileSide.None) { continue; }
|
||||
if (side == TileSide.None)
|
||||
{
|
||||
oldVines.FailedGrowthAttempts++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (GrowthWeights != Vector4.One)
|
||||
{
|
||||
var (x, y, z, w) = GrowthWeights;
|
||||
float[] weights = { x, y, z, w };
|
||||
int index = (int) Math.Log2((int) side);
|
||||
if (MathUtils.NearlyEqual(weights[index], 0f))
|
||||
{
|
||||
oldVines.FailedGrowthAttempts++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 pos = oldVines.AdjacentPositions[side];
|
||||
Rectangle rect = VineTile.CreatePlantRect(pos);
|
||||
@@ -705,8 +745,7 @@ namespace Barotrauma.Items.Components
|
||||
// if the X value is bigger than Y it's to the left or right of us and then check if X is negative or positive to determine if it's right or left
|
||||
TileSide connectingSide = absDistX > absDistY ? distX > 0 ? TileSide.Right : TileSide.Left : distY > 0 ? TileSide.Top : TileSide.Bottom;
|
||||
|
||||
// We use log2 to find the index and offset that index by 2 since the opposite side is always 2 offsets away
|
||||
TileSide oppositeSide = (TileSide) (1 << ((int) Math.Log2((int) connectingSide) + 2) % 4);
|
||||
TileSide oppositeSide = connectingSide.GetOppositeSide();
|
||||
|
||||
if (otherVine.BlockedSides.IsBitSet(connectingSide))
|
||||
{
|
||||
@@ -810,9 +849,9 @@ namespace Barotrauma.Items.Components
|
||||
return element;
|
||||
}
|
||||
|
||||
public override void Load(XElement componentElement, bool usePrefabValues)
|
||||
public override void Load(XElement componentElement, bool usePrefabValues, IdRemap idRemap)
|
||||
{
|
||||
base.Load(componentElement, usePrefabValues);
|
||||
base.Load(componentElement, usePrefabValues, idRemap);
|
||||
flowerTiles = componentElement.GetAttributeIntArray("flowertiles", new int[0]);
|
||||
Decayed = componentElement.GetAttributeBool("decayed", false);
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace Barotrauma.Items.Components
|
||||
private float swingState;
|
||||
|
||||
private bool attachable, attached, attachedByDefault;
|
||||
private Voronoi2.VoronoiCell attachTargetCell;
|
||||
private readonly PhysicsBody body;
|
||||
public PhysicsBody Pusher
|
||||
{
|
||||
@@ -213,9 +214,9 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override void Load(XElement componentElement, bool usePrefabValues)
|
||||
public override void Load(XElement componentElement, bool usePrefabValues, IdRemap idRemap)
|
||||
{
|
||||
base.Load(componentElement, usePrefabValues);
|
||||
base.Load(componentElement, usePrefabValues, idRemap);
|
||||
|
||||
if (usePrefabValues)
|
||||
{
|
||||
@@ -255,6 +256,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)
|
||||
{
|
||||
@@ -359,7 +361,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void Unequip(Character character)
|
||||
{
|
||||
if (picker == null) return;
|
||||
if (picker == null) { return; }
|
||||
|
||||
picker.DeselectItem(item);
|
||||
#if SERVER
|
||||
@@ -383,9 +385,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; }
|
||||
@@ -406,7 +408,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (item.CurrentHull == null)
|
||||
{
|
||||
return Structure.GetAttachTarget(item.WorldPosition) != null;
|
||||
return attachTargetCell != null && Structure.GetAttachTarget(item.WorldPosition) != null;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -464,7 +466,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,6 +481,11 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
item.Submarine = attachTarget.Submarine;
|
||||
}
|
||||
else
|
||||
{
|
||||
attachTargetCell = GetAttachTargetCell(150.0f);
|
||||
if (attachTargetCell != null) { IsActive = true; }
|
||||
}
|
||||
}
|
||||
|
||||
var containedItems = item.OwnInventory?.Items;
|
||||
@@ -507,6 +514,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 +576,47 @@ 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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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 +626,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)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class IdCard : Pickable
|
||||
{
|
||||
public IdCard(Item item, XElement element) : base(item, element)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Initialize(CharacterInfo info)
|
||||
{
|
||||
if (info == null) return;
|
||||
|
||||
if (info.Job?.Prefab != null)
|
||||
{
|
||||
item.AddTag("jobid:" + info.Job.Prefab.Identifier);
|
||||
}
|
||||
|
||||
var head = info.Head;
|
||||
|
||||
if (info != null && head != null)
|
||||
{
|
||||
item.AddTag("gender:" + head.gender.ToString().ToLowerInvariant());
|
||||
item.AddTag("race:" + head.race.ToString());
|
||||
item.AddTag("headspriteid:" + info.HeadSpriteId.ToString());
|
||||
item.AddTag("hairindex:" + head.HairIndex);
|
||||
item.AddTag("beardindex:" + head.BeardIndex);
|
||||
item.AddTag("moustacheindex:" + head.MoustacheIndex);
|
||||
item.AddTag("faceattachmentindex:" + head.FaceAttachmentIndex);
|
||||
|
||||
if (head.SheetIndex != null)
|
||||
{
|
||||
item.AddTag("sheetindex:" + head.SheetIndex.Value.X + ";" + head.SheetIndex.Value.Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Equip(Character character)
|
||||
{
|
||||
base.Equip(character);
|
||||
character.Info.CheckDisguiseStatus(true, this);
|
||||
}
|
||||
|
||||
public override void Unequip(Character character)
|
||||
{
|
||||
base.Unequip(character);
|
||||
character.Info.CheckDisguiseStatus(true, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,9 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (holdable == null) { return; }
|
||||
|
||||
deattachTimer = Math.Max(0.0f, value);
|
||||
#if SERVER
|
||||
if (deattachTimer >= DeattachDuration)
|
||||
@@ -57,7 +60,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public bool Attached
|
||||
{
|
||||
get { return holdable == null ? false : holdable.Attached; }
|
||||
get { return holdable != null && holdable.Attached; }
|
||||
}
|
||||
|
||||
public LevelResource(Item item, XElement element) : base(item, element)
|
||||
@@ -67,14 +70,14 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (!holdable.Attached)
|
||||
if (holdable != null && !holdable.Attached)
|
||||
{
|
||||
trigger.Enabled = false;
|
||||
IsActive = false;
|
||||
}
|
||||
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);
|
||||
}
|
||||
@@ -87,7 +90,6 @@ namespace Barotrauma.Items.Components
|
||||
holdable = item.GetComponent<Holdable>();
|
||||
if (holdable == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error while initializing item \"" + item.Name + "\". Level resources require a Holdable component.");
|
||||
IsActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -143,13 +143,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.HasSelectedItem(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; }
|
||||
|
||||
+13
-47
@@ -5,6 +5,7 @@ using FarseerPhysics.Dynamics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
@@ -128,54 +129,19 @@ namespace Barotrauma.Items.Components
|
||||
for (int i = 0; i < ProjectileCount; i++)
|
||||
{
|
||||
Projectile projectile = FindProjectile(triggerOnUseOnContainers: true);
|
||||
if (projectile == null) { return true; }
|
||||
|
||||
float spread = GetSpread(character);
|
||||
float rotation = (item.body.Dir == 1.0f) ? item.body.Rotation : item.body.Rotation - MathHelper.Pi;
|
||||
rotation += spread * Rand.Range(-0.5f, 0.5f);
|
||||
|
||||
projectile.User = character;
|
||||
//add the limbs of the shooter to the list of bodies to be ignored
|
||||
//so that the player can't shoot himself
|
||||
projectile.IgnoredBodies = new List<Body>(limbBodies);
|
||||
|
||||
Vector2 projectilePos = item.SimPosition;
|
||||
Vector2 sourcePos = character?.AnimController == null ? item.SimPosition : character.AnimController.AimSourceSimPos;
|
||||
Vector2 barrelPos = TransformedBarrelPos + item.body.SimPosition;
|
||||
//make sure there's no obstacles between the base of the weapon (or the shoulder of the character) and the end of the barrel
|
||||
if (Submarine.PickBody(sourcePos, barrelPos, projectile.IgnoredBodies, Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionItemBlocking) == null)
|
||||
if (projectile != null)
|
||||
{
|
||||
//no obstacles -> we can spawn the projectile at the barrel
|
||||
projectilePos = barrelPos;
|
||||
}
|
||||
else if ((sourcePos - barrelPos).LengthSquared() > 0.0001f)
|
||||
{
|
||||
//spawn the projectile body.GetMaxExtent() away from the position where the raycast hit the obstacle
|
||||
projectilePos = sourcePos - Vector2.Normalize(barrelPos - projectilePos) * Math.Max(projectile.Item.body.GetMaxExtent(), 0.1f);
|
||||
}
|
||||
|
||||
projectile.Item.body.ResetDynamics();
|
||||
projectile.Item.SetTransform(projectilePos, rotation);
|
||||
|
||||
projectile.Use(deltaTime);
|
||||
projectile.Item.GetComponent<Rope>()?.Attach(item, projectile.Item);
|
||||
if (projectile.Item.Removed) { continue; }
|
||||
projectile.User = character;
|
||||
|
||||
projectile.Item.body.ApplyTorque(projectile.Item.body.Mass * degreeOfFailure * Rand.Range(-10.0f, 10.0f));
|
||||
|
||||
//set the rotation of the projectile again because dropping the projectile resets the rotation
|
||||
projectile.Item.SetTransform(projectilePos,
|
||||
rotation + (projectile.Item.body.Dir * projectile.LaunchRotationRadians));
|
||||
|
||||
item.RemoveContained(projectile.Item);
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
//recoil
|
||||
item.body.ApplyLinearImpulse(
|
||||
new Vector2((float)Math.Cos(projectile.Item.body.Rotation), (float)Math.Sin(projectile.Item.body.Rotation)) * item.body.Mass * -50.0f,
|
||||
maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
|
||||
Vector2 barrelPos = TransformedBarrelPos + item.body.SimPosition;
|
||||
float rotation = (Item.body.Dir == 1.0f) ? Item.body.Rotation : Item.body.Rotation - MathHelper.Pi;
|
||||
float spread = GetSpread(character) * Rand.Range(-0.5f, 0.5f);
|
||||
projectile.Shoot(character, character.AnimController.AimSourceSimPos, barrelPos, rotation + spread, ignoredBodies: limbBodies.ToList(), createNetworkEvent: false);
|
||||
projectile.Item.GetComponent<Rope>()?.Attach(Item, projectile.Item);
|
||||
if (i == 0)
|
||||
{
|
||||
Item.body.ApplyLinearImpulse(new Vector2((float)Math.Cos(projectile.Item.body.Rotation), (float)Math.Sin(projectile.Item.body.Rotation)) * Item.body.Mass * -50.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
|
||||
}
|
||||
projectile.Item.body.ApplyTorque(projectile.Item.body.Mass * degreeOfFailure * Rand.Range(-10.0f, 10.0f));
|
||||
Item.RemoveContained(projectile.Item);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.MapCreatures.Behavior;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -53,6 +54,18 @@ namespace Barotrauma.Items.Components
|
||||
get; set;
|
||||
}
|
||||
|
||||
[Serialize(0.0f, false, description: "How much damage is applied to ballast flora.")]
|
||||
public float FireDamage
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
[Serialize(0.0f, false, description: "How many units of damage the item removes from destructible level walls per second.")]
|
||||
public float LevelWallFixAmount
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
[Serialize(0.0f, false, description: "How much the item decreases the size of fires per second.")]
|
||||
public float ExtinguishAmount
|
||||
{
|
||||
@@ -183,23 +196,40 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
Vector2 rayStart;
|
||||
Vector2 rayStartWorld;
|
||||
Vector2 sourcePos = character?.AnimController == null ? item.SimPosition : character.AnimController.AimSourceSimPos;
|
||||
Vector2 barrelPos = item.SimPosition + ConvertUnits.ToSimUnits(TransformedBarrelPos);
|
||||
//make sure there's no obstacles between the base of the item (or the shoulder of the character) and the end of the barrel
|
||||
if (Submarine.PickBody(sourcePos, barrelPos, collisionCategory: Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionItemBlocking) == null)
|
||||
{
|
||||
//no obstacles -> we start the raycast at the end of the barrel
|
||||
rayStart = ConvertUnits.ToSimUnits(item.WorldPosition + TransformedBarrelPos);
|
||||
rayStart = ConvertUnits.ToSimUnits(item.Position + TransformedBarrelPos);
|
||||
rayStartWorld = ConvertUnits.ToSimUnits(item.WorldPosition + TransformedBarrelPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
rayStart = Submarine.LastPickedPosition + Submarine.LastPickedNormal * 0.1f;
|
||||
if (item.Submarine != null) { rayStart += item.Submarine.SimPosition; }
|
||||
rayStart = rayStartWorld = Submarine.LastPickedPosition + Submarine.LastPickedNormal * 0.1f;
|
||||
if (item.Submarine != null) { rayStartWorld += item.Submarine.SimPosition; }
|
||||
}
|
||||
|
||||
//if the calculated barrel pos is in another hull, use the origin of the item to make sure the particles don't end up in an incorrect hull
|
||||
if (item.CurrentHull != null)
|
||||
{
|
||||
var barrelHull = Hull.FindHull(ConvertUnits.ToDisplayUnits(rayStartWorld), item.CurrentHull, useWorldCoordinates: true);
|
||||
if (barrelHull != null && barrelHull != item.CurrentHull)
|
||||
{
|
||||
if (MathUtils.GetLineRectangleIntersection(ConvertUnits.ToDisplayUnits(sourcePos), ConvertUnits.ToDisplayUnits(rayStart), item.CurrentHull.Rect, out Vector2 hullIntersection))
|
||||
{
|
||||
Vector2 rayDir = rayStart.NearlyEquals(sourcePos) ? Vector2.Zero : Vector2.Normalize(rayStart - sourcePos);
|
||||
rayStartWorld = ConvertUnits.ToSimUnits(hullIntersection - rayDir * 5.0f);
|
||||
if (item.Submarine != null) { rayStartWorld += item.Submarine.SimPosition; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 = rayStart +
|
||||
Vector2 rayEnd = rayStartWorld +
|
||||
ConvertUnits.ToSimUnits(new Vector2(
|
||||
(float)Math.Cos(angle),
|
||||
(float)Math.Sin(angle)) * Range * item.body.Dir);
|
||||
@@ -218,7 +248,7 @@ namespace Barotrauma.Items.Components
|
||||
IsActive = true;
|
||||
activeTimer = 0.1f;
|
||||
|
||||
debugRayStartPos = ConvertUnits.ToDisplayUnits(rayStart);
|
||||
debugRayStartPos = ConvertUnits.ToDisplayUnits(rayStartWorld);
|
||||
debugRayEndPos = ConvertUnits.ToDisplayUnits(rayEnd);
|
||||
|
||||
Submarine parentSub = character?.Submarine ?? item.Submarine;
|
||||
@@ -232,16 +262,16 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Repair(rayStart - sub.SimPosition, rayEnd - sub.SimPosition, deltaTime, character, degreeOfSuccess, ignoredBodies);
|
||||
Repair(rayStartWorld - sub.SimPosition, rayEnd - sub.SimPosition, deltaTime, character, degreeOfSuccess, ignoredBodies);
|
||||
}
|
||||
Repair(rayStart, rayEnd, deltaTime, character, degreeOfSuccess, ignoredBodies);
|
||||
Repair(rayStartWorld, rayEnd, deltaTime, character, degreeOfSuccess, ignoredBodies);
|
||||
}
|
||||
else
|
||||
{
|
||||
Repair(rayStart - parentSub.SimPosition, rayEnd - parentSub.SimPosition, deltaTime, character, degreeOfSuccess, ignoredBodies);
|
||||
Repair(rayStartWorld - parentSub.SimPosition, rayEnd - parentSub.SimPosition, deltaTime, character, degreeOfSuccess, ignoredBodies);
|
||||
}
|
||||
|
||||
UseProjSpecific(deltaTime, rayStart);
|
||||
UseProjSpecific(deltaTime, rayStartWorld);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -289,6 +319,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (RepairThroughHoles && f.IsSensor && f.Body?.UserData is Structure || (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;
|
||||
},
|
||||
allowInsideFixture: true);
|
||||
@@ -324,9 +355,16 @@ namespace Barotrauma.Items.Components
|
||||
hitCharacters.Add(hitCharacter);
|
||||
}
|
||||
|
||||
//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)
|
||||
float thisBodyFraction = Submarine.LastPickedBodyDist(body);
|
||||
if (!RepairThroughWalls && lastHitType == typeof(Structure) && Range * (thisBodyFraction - lastPickedFraction) > 100.0f)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (FixBody(user, deltaTime, degreeOfSuccess, body))
|
||||
{
|
||||
lastPickedFraction = Submarine.LastPickedBodyDist(body);
|
||||
lastPickedFraction = thisBodyFraction;
|
||||
if (bodyType != null) { lastHitType = bodyType; }
|
||||
}
|
||||
}
|
||||
@@ -341,6 +379,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (RepairThroughHoles && f.IsSensor && f.Body?.UserData is Structure) { return false; }
|
||||
if (f.Body?.UserData as string == "ruinroom") { return false; }
|
||||
if (f.Body?.UserData is VineTile && !(FireDamage > 0)) { return false; }
|
||||
|
||||
if (f.Body?.UserData is Item targetItem)
|
||||
{
|
||||
if (!HitItems) { return false; }
|
||||
@@ -479,6 +519,15 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
return true;
|
||||
}
|
||||
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)
|
||||
{
|
||||
levelWall.AddDamage(-LevelWallFixAmount * deltaTime, item.WorldPosition);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (targetBody.UserData is Character targetCharacter)
|
||||
{
|
||||
if (targetCharacter.Removed) { return false; }
|
||||
@@ -569,6 +618,13 @@ namespace Barotrauma.Items.Components
|
||||
FixItemProjSpecific(user, deltaTime, targetItem);
|
||||
return true;
|
||||
}
|
||||
else if (targetBody.UserData is BallastFloraBranch branch)
|
||||
{
|
||||
if (branch.ParentBallastFlora is { } ballastFlora)
|
||||
{
|
||||
ballastFlora.DamageBranch(branch, FireDamage * deltaTime, BallastFloraBehavior.AttackType.Fire, user);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -769,7 +825,8 @@ namespace Barotrauma.Items.Components
|
||||
object value = property.GetValue(target);
|
||||
if (door.Stuck > 0)
|
||||
{
|
||||
var progressBar = user.UpdateHUDProgressBar(door, door.Item.WorldPosition, door.Stuck / 100, Color.DarkGray * 0.5f, Color.White, "progressbar.welding");
|
||||
var progressBar = user.UpdateHUDProgressBar(door, door.Item.WorldPosition, door.Stuck / 100, Color.DarkGray * 0.5f, Color.White,
|
||||
effect.propertyEffects[i].GetType() == typeof(float) && (float)effect.propertyEffects[i] < 0 ? "progressbar.cutting" : "progressbar.welding");
|
||||
if (progressBar != null) { progressBar.Size = new Vector2(60.0f, 20.0f); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -394,7 +394,10 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
//called when isActive is true and condition > 0.0f
|
||||
public virtual void Update(float deltaTime, Camera cam) { }
|
||||
public virtual void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime);
|
||||
}
|
||||
|
||||
//called when isActive is true and condition == 0.0f
|
||||
public virtual void UpdateBroken(float deltaTime, Camera cam)
|
||||
@@ -763,7 +766,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Load(XElement componentElement, bool usePrefabValues)
|
||||
public virtual void Load(XElement componentElement, bool usePrefabValues, IdRemap idRemap)
|
||||
{
|
||||
if (componentElement != null)
|
||||
{
|
||||
@@ -963,12 +966,12 @@ namespace Barotrauma.Items.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
protected AIObjectiveContainItem AIContainItems<T>(ItemContainer container, Character character, AIObjective objective, int itemCount, bool equip, bool removeEmpty, bool spawnItemIfNotFound = false) where T : ItemComponent
|
||||
protected AIObjectiveContainItem AIContainItems<T>(ItemContainer container, Character character, AIObjective currentObjective, int itemCount, bool equip, bool removeEmpty, bool spawnItemIfNotFound = false, bool dropItemOnDeselected = false) where T : ItemComponent
|
||||
{
|
||||
AIObjectiveContainItem containObjective = null;
|
||||
if (character.AIController is HumanAIController aiController)
|
||||
{
|
||||
containObjective = new AIObjectiveContainItem(character, container.GetContainableItemIdentifiers.ToArray(), container, objective.objectiveManager, spawnItemIfNotFound: spawnItemIfNotFound)
|
||||
containObjective = new AIObjectiveContainItem(character, container.GetContainableItemIdentifiers.ToArray(), container, currentObjective.objectiveManager, spawnItemIfNotFound: spawnItemIfNotFound)
|
||||
{
|
||||
targetItemCount = itemCount,
|
||||
Equip = equip,
|
||||
@@ -986,11 +989,21 @@ namespace Barotrauma.Items.Components
|
||||
return 1.0f;
|
||||
}
|
||||
};
|
||||
containObjective.Abandoned += () =>
|
||||
containObjective.Abandoned += () => aiController.IgnoredItems.Add(container.Item);
|
||||
if (dropItemOnDeselected)
|
||||
{
|
||||
aiController.IgnoredItems.Add(container.Item);
|
||||
};
|
||||
objective.AddSubObjective(containObjective);
|
||||
currentObjective.Deselected += () =>
|
||||
{
|
||||
if (containObjective == null) { return; }
|
||||
if (containObjective.IsCompleted) { return; }
|
||||
Item item = containObjective.ItemToContain;
|
||||
if (item != null && character.CanInteractWith(item, checkLinked: false))
|
||||
{
|
||||
item.Drop(character);
|
||||
}
|
||||
};
|
||||
}
|
||||
currentObjective.AddSubObjective(containObjective);
|
||||
}
|
||||
return containObjective;
|
||||
}
|
||||
@@ -1011,6 +1024,7 @@ namespace Barotrauma.Items.Components
|
||||
if (FindSuitableContainer(character,
|
||||
i =>
|
||||
{
|
||||
if (i.IsThisOrAnyContainerIgnoredByAI()) { return 0; }
|
||||
var container = i.GetComponent<ItemContainer>();
|
||||
if (container == null) { return 0; }
|
||||
if (container.Inventory.IsFull()) { return 0; }
|
||||
|
||||
@@ -94,6 +94,9 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, false)]
|
||||
public bool RemoveContainedItemsOnDeconstruct { get; set; }
|
||||
|
||||
public bool ShouldBeContained(string[] identifiersOrTags, out bool isRestrictionsDefined)
|
||||
{
|
||||
isRestrictionsDefined = containableRestrictions.Any();
|
||||
@@ -377,12 +380,9 @@ namespace Barotrauma.Items.Components
|
||||
if (SpawnWithId.Length > 0)
|
||||
{
|
||||
ItemPrefab prefab = ItemPrefab.Prefabs.Find(m => m.Identifier == SpawnWithId);
|
||||
if (prefab != null)
|
||||
if (prefab != null && Inventory != null && Inventory.Items.Any(it => it == null))
|
||||
{
|
||||
if (Inventory != null && Inventory.Items.Any(it => it == null))
|
||||
{
|
||||
Entity.Spawner?.AddToSpawnQueue(prefab, Inventory);
|
||||
}
|
||||
Entity.Spawner?.AddToSpawnQueue(prefab, Inventory, spawnIfInventoryFull: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -415,17 +415,17 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override void Load(XElement componentElement, bool usePrefabValues)
|
||||
public override void Load(XElement componentElement, bool usePrefabValues, IdRemap idRemap)
|
||||
{
|
||||
base.Load(componentElement, usePrefabValues);
|
||||
base.Load(componentElement, usePrefabValues, idRemap);
|
||||
|
||||
string containedString = componentElement.GetAttributeString("contained", "");
|
||||
string[] itemIdStrings = containedString.Split(',');
|
||||
itemIds = new ushort[itemIdStrings.Length];
|
||||
for (int i = 0; i < itemIdStrings.Length; i++)
|
||||
{
|
||||
if (!ushort.TryParse(itemIdStrings[i], out ushort id)) { continue; }
|
||||
itemIds[i] = id;
|
||||
if (!int.TryParse(itemIdStrings[i], out int id)) { continue; }
|
||||
itemIds[i] = idRemap.GetOffsetId(id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -318,16 +318,12 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (!character.IsRemotePlayer || character.ViewTarget == focusTarget)
|
||||
{
|
||||
Vector2 centerPos = new Vector2(item.WorldRect.Center.X, item.WorldRect.Center.Y);
|
||||
Vector2 centerPos = new Vector2(focusTarget.WorldRect.Center.X, focusTarget.WorldRect.Center.Y);
|
||||
|
||||
Item targetItem = focusTarget as Item;
|
||||
if (targetItem != null)
|
||||
Turret turret = focusTarget.GetComponent<Turret>();
|
||||
if (turret != null)
|
||||
{
|
||||
Turret turret = targetItem.GetComponent<Turret>();
|
||||
if (turret != null)
|
||||
{
|
||||
centerPos = new Vector2(targetItem.WorldRect.X + turret.TransformedBarrelPos.X, targetItem.WorldRect.Y - turret.TransformedBarrelPos.Y);
|
||||
}
|
||||
centerPos = new Vector2(focusTarget.WorldRect.X + turret.TransformedBarrelPos.X, focusTarget.WorldRect.Y - turret.TransformedBarrelPos.Y);
|
||||
}
|
||||
|
||||
Vector2 offset = character.CursorWorldPosition - centerPos;
|
||||
@@ -356,6 +352,9 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override bool Pick(Character picker)
|
||||
{
|
||||
#if CLIENT
|
||||
if (Screen.Selected == GameMain.SubEditorScreen) { return false; }
|
||||
#endif
|
||||
if (IsToggle)
|
||||
{
|
||||
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
|
||||
|
||||
@@ -123,7 +123,7 @@ namespace Barotrauma.Items.Components
|
||||
//drop all items that are inside the deconstructed item
|
||||
foreach (ItemContainer ic in targetItem.GetComponents<ItemContainer>())
|
||||
{
|
||||
if (ic?.Inventory?.Items == null) { continue; }
|
||||
if (ic?.Inventory?.Items == null || ic.RemoveContainedItemsOnDeconstruct) { continue; }
|
||||
foreach (Item containedItem in ic.Inventory.Items)
|
||||
{
|
||||
containedItem?.Drop(dropper: null, createNetworkEvent: true);
|
||||
|
||||
@@ -113,7 +113,7 @@ namespace Barotrauma.Items.Components
|
||||
forceMultiplier *= MathHelper.Lerp(0.5f, 2.0f, (float)Math.Sqrt(User.GetSkillLevel("helm") / 100));
|
||||
}
|
||||
|
||||
float voltageFactor = MinVoltage <= 0.0f ? 1.0f : Math.Min(Voltage / MinVoltage, 1.0f);
|
||||
float voltageFactor = MinVoltage <= 0.0f ? 1.0f : Math.Min(Voltage, 1.0f);
|
||||
Vector2 currForce = new Vector2(force * maxForce * forceMultiplier * voltageFactor, 0.0f);
|
||||
//less effective when in a bad condition
|
||||
currForce *= MathHelper.Lerp(0.5f, 2.0f, item.Condition / item.MaxCondition);
|
||||
@@ -121,7 +121,7 @@ namespace Barotrauma.Items.Components
|
||||
UpdatePropellerDamage(deltaTime);
|
||||
float maxChangeSpeed = 0.5f;
|
||||
float modifier = 2;
|
||||
float noise = currForce.Length() * forceMultiplier * modifier / maxForce;
|
||||
float noise = MathUtils.NearlyEqual(0.0f, maxForce) ? 0.0f : currForce.Length() * forceMultiplier * modifier / maxForce;
|
||||
float min = Math.Max(1 - maxChangeSpeed, 0);
|
||||
float max = 1 + maxChangeSpeed;
|
||||
UpdateAITargets(Math.Clamp(noise, min, max), deltaTime);
|
||||
|
||||
@@ -482,9 +482,9 @@ namespace Barotrauma.Items.Components
|
||||
return componentElement;
|
||||
}
|
||||
|
||||
public override void Load(XElement componentElement, bool usePrefabValues)
|
||||
public override void Load(XElement componentElement, bool usePrefabValues, IdRemap idRemap)
|
||||
{
|
||||
base.Load(componentElement, usePrefabValues);
|
||||
base.Load(componentElement, usePrefabValues, idRemap);
|
||||
savedFabricatedItem = componentElement.GetAttributeString("fabricateditemidentifier", "");
|
||||
savedTimeUntilReady = componentElement.GetAttributeFloat("savedtimeuntilready", 0.0f);
|
||||
savedRequiredTime = componentElement.GetAttributeFloat("savedrequiredtime", 0.0f);
|
||||
|
||||
+17
-23
@@ -8,11 +8,10 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
class OxygenGenerator : Powered
|
||||
{
|
||||
private float powerDownTimer;
|
||||
|
||||
private float generatedAmount;
|
||||
|
||||
private List<Vent> ventList;
|
||||
//key = vent, float = total volume of the hull the vent is in and the hulls connected to it
|
||||
private Dictionary<Vent, float> ventList;
|
||||
|
||||
private float totalHullVolume;
|
||||
|
||||
@@ -49,17 +48,12 @@ namespace Barotrauma.Items.Components
|
||||
Voltage = 1.0f;
|
||||
}
|
||||
|
||||
if (item.CurrentHull == null) return;
|
||||
if (item.CurrentHull == null) { return; }
|
||||
|
||||
if (Voltage < MinVoltage)
|
||||
{
|
||||
powerDownTimer += deltaTime;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
powerDownTimer = 0.0f;
|
||||
}
|
||||
|
||||
CurrFlow = Math.Min(Voltage, 1.0f) * generatedAmount * 100.0f;
|
||||
|
||||
@@ -76,24 +70,25 @@ namespace Barotrauma.Items.Components
|
||||
public override void UpdateBroken(float deltaTime, Camera cam)
|
||||
{
|
||||
base.UpdateBroken(deltaTime, cam);
|
||||
powerDownTimer += deltaTime;
|
||||
CurrFlow = 0.0f;
|
||||
}
|
||||
|
||||
private void GetVents()
|
||||
{
|
||||
ventList.Clear();
|
||||
|
||||
ventList = new Dictionary<Vent, float>();
|
||||
foreach (MapEntity entity in item.linkedTo)
|
||||
{
|
||||
Item linkedItem = entity as Item;
|
||||
if (linkedItem == null) continue;
|
||||
if (!(entity is Item linkedItem)) { continue; }
|
||||
|
||||
Vent vent = linkedItem.GetComponent<Vent>();
|
||||
if (vent == null) continue;
|
||||
if (vent?.Item.CurrentHull == null) { continue; }
|
||||
|
||||
ventList.Add(vent);
|
||||
if (linkedItem.CurrentHull != null) totalHullVolume += linkedItem.CurrentHull.Volume;
|
||||
ventList.Add(vent, 0.0f);
|
||||
foreach (Hull connectedHull in vent.Item.CurrentHull.GetConnectedHulls(includingThis: true, searchDepth: 10, ignoreClosedGaps: true))
|
||||
{
|
||||
totalHullVolume += connectedHull.Volume;
|
||||
ventList[vent] += connectedHull.Volume;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,18 +96,17 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (ventList == null)
|
||||
{
|
||||
ventList = new List<Vent>();
|
||||
GetVents();
|
||||
}
|
||||
|
||||
if (!ventList.Any() || totalHullVolume <= 0.0f) return;
|
||||
if (!ventList.Any() || totalHullVolume <= 0.0f) { return; }
|
||||
|
||||
foreach (Vent v in ventList)
|
||||
foreach (KeyValuePair<Vent, float> v in ventList)
|
||||
{
|
||||
if (v.Item.CurrentHull == null) continue;
|
||||
if (v.Key?.Item.CurrentHull == null) { continue; }
|
||||
|
||||
v.OxygenFlow = deltaOxygen * (v.Item.CurrentHull.Volume / totalHullVolume);
|
||||
v.IsActive = true;
|
||||
v.Key.OxygenFlow = deltaOxygen * (v.Value / totalHullVolume);
|
||||
v.Key.IsActive = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.MapCreatures.Behavior;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -11,10 +13,38 @@ namespace Barotrauma.Items.Components
|
||||
private float flowPercentage;
|
||||
private float maxFlow;
|
||||
|
||||
private float? targetLevel;
|
||||
public float? TargetLevel;
|
||||
|
||||
private bool hijacked;
|
||||
public bool Hijacked
|
||||
{
|
||||
get { return hijacked; }
|
||||
set
|
||||
{
|
||||
if (value == hijacked) { return; }
|
||||
hijacked = value;
|
||||
#if SERVER
|
||||
item.CreateServerEvent(this);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private float pumpSpeedLockTimer, isActiveLockTimer;
|
||||
|
||||
private bool infected;
|
||||
|
||||
[Serialize(false, true, description: "Whether or not the pump is infected with ballast flora spores.")]
|
||||
public bool Infected
|
||||
{
|
||||
get => infected;
|
||||
set
|
||||
{
|
||||
infected = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string InfectIdentifier;
|
||||
|
||||
[Serialize(0.0f, true, description: "How fast the item is currently pumping water (-100 = full speed out, 100 = full speed in). Intended to be used by StatusEffect conditionals (setting this value in XML has no effect).")]
|
||||
public float FlowPercentage
|
||||
{
|
||||
@@ -66,12 +96,12 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
currFlow = 0.0f;
|
||||
|
||||
if (targetLevel != null)
|
||||
if (TargetLevel != null)
|
||||
{
|
||||
pumpSpeedLockTimer -= deltaTime;
|
||||
float hullPercentage = 0.0f;
|
||||
if (item.CurrentHull != null) { hullPercentage = (item.CurrentHull.WaterVolume / item.CurrentHull.Volume) * 100.0f; }
|
||||
FlowPercentage = ((float)targetLevel - hullPercentage) * 10.0f;
|
||||
FlowPercentage = ((float)TargetLevel - hullPercentage) * 10.0f;
|
||||
}
|
||||
|
||||
currPowerConsumption = powerConsumption * Math.Abs(flowPercentage / 100.0f);
|
||||
@@ -92,14 +122,41 @@ namespace Barotrauma.Items.Components
|
||||
//less effective when in a bad condition
|
||||
currFlow *= MathHelper.Lerp(0.5f, 1.0f, item.Condition / item.MaxCondition);
|
||||
|
||||
|
||||
if (currFlow < 0 && Infected)
|
||||
{
|
||||
InfectBallast(InfectIdentifier);
|
||||
}
|
||||
Infected = false;
|
||||
|
||||
item.CurrentHull.WaterVolume += currFlow;
|
||||
if (item.CurrentHull.WaterVolume > item.CurrentHull.Volume) { item.CurrentHull.Pressure += 0.5f; }
|
||||
}
|
||||
|
||||
public void InfectBallast(string identifier)
|
||||
{
|
||||
Hull hull = item.CurrentHull;
|
||||
if (hull == null) { return; }
|
||||
|
||||
// if the ship is already infected then do nothing
|
||||
if (Hull.hullList.Where(h => h.Submarine == hull.Submarine).Any(h => h.BallastFlora != null)) { return; }
|
||||
|
||||
if (hull.BallastFlora != null) { return; }
|
||||
|
||||
Vector2 offset = item.WorldPosition - hull.WorldPosition;
|
||||
hull.BallastFlora = new BallastFloraBehavior(hull, BallastFloraPrefab.Find(identifier), offset, firstGrowth: true);
|
||||
|
||||
#if SERVER
|
||||
hull.BallastFlora.SendNetworkMessage(hull.BallastFlora, BallastFloraBehavior.NetworkHeader.Spawn);
|
||||
#endif
|
||||
}
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime);
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0.0f, float signalStrength = 1.0f)
|
||||
{
|
||||
if (Hijacked) { return; }
|
||||
|
||||
if (connection.Name == "toggle")
|
||||
{
|
||||
IsActive = !IsActive;
|
||||
@@ -115,7 +172,7 @@ namespace Barotrauma.Items.Components
|
||||
if (float.TryParse(signal, NumberStyles.Any, CultureInfo.InvariantCulture, out float tempSpeed))
|
||||
{
|
||||
flowPercentage = MathHelper.Clamp(tempSpeed, -100.0f, 100.0f);
|
||||
targetLevel = null;
|
||||
TargetLevel = null;
|
||||
pumpSpeedLockTimer = 0.1f;
|
||||
}
|
||||
}
|
||||
@@ -123,7 +180,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (float.TryParse(signal, NumberStyles.Any, CultureInfo.InvariantCulture, out float tempTarget))
|
||||
{
|
||||
targetLevel = MathHelper.Clamp(tempTarget + 50.0f, 0.0f, 100.0f);
|
||||
TargetLevel = MathHelper.Clamp(tempTarget + 50.0f, 0.0f, 100.0f);
|
||||
pumpSpeedLockTimer = 0.1f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(0.2f, true, description: "How fast the condition of the contained fuel rods deteriorates per second."), Editable(0.0f, 1000.0f)]
|
||||
[Serialize(0.2f, true, description: "How fast the condition of the contained fuel rods deteriorates per second."), Editable(0.0f, 1000.0f, decimals: 3)]
|
||||
public float FuelConsumptionRate
|
||||
{
|
||||
get { return fuelConsumptionRate; }
|
||||
@@ -399,6 +399,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
//fission rate is clamped to the amount of available fuel
|
||||
float maxFissionRate = Math.Min(prevAvailableFuel, 100.0f);
|
||||
if (maxFissionRate >= 100.0f) { return false; }
|
||||
|
||||
float maxTurbineOutput = 100.0f;
|
||||
|
||||
//calculate the maximum output if the fission rate is cranked as high as it goes and turbine output is at max
|
||||
@@ -589,7 +591,7 @@ namespace Barotrauma.Items.Components
|
||||
if (objective.SubObjectives.None())
|
||||
{
|
||||
int itemCount = item.ContainedItems.Count(i => i != null && container.ContainableItems.Any(ri => ri.MatchesItem(i))) + 1;
|
||||
AIContainItems<Reactor>(container, character, objective, itemCount, equip: false, removeEmpty: true, spawnItemIfNotFound: character.TeamID == Character.TeamType.FriendlyNPC);
|
||||
AIContainItems<Reactor>(container, character, objective, itemCount, equip: false, removeEmpty: true, spawnItemIfNotFound: character.TeamID == Character.TeamType.FriendlyNPC, dropItemOnDeselected: true);
|
||||
character.Speak(TextManager.Get("DialogReactorFuel"), null, 0.0f, "reactorfuel", 30.0f);
|
||||
}
|
||||
return false;
|
||||
@@ -604,10 +606,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (item != null && container.ContainableItems.Any(ri => ri.MatchesItem(item)))
|
||||
{
|
||||
if (!character.Inventory.TryPutItem(item, character, allowedSlots: item.AllowedSlots))
|
||||
{
|
||||
item.Drop(character);
|
||||
}
|
||||
item.Drop(character);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private bool useDirectionalPing = false;
|
||||
private Vector2 pingDirection = new Vector2(1.0f, 0.0f);
|
||||
private bool useMineralScanner;
|
||||
|
||||
private bool aiPingCheckPending;
|
||||
|
||||
@@ -103,6 +104,10 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, false, description: "Does the sonar have mineral scanning mode. " +
|
||||
"Only available in-game when the Item has no Steering component.")]
|
||||
public bool HasMineralScanner { get; set; }
|
||||
|
||||
public float Zoom
|
||||
{
|
||||
get { return zoom; }
|
||||
@@ -343,6 +348,7 @@ namespace Barotrauma.Items.Components
|
||||
bool isActive = msg.ReadBoolean();
|
||||
bool directionalPing = useDirectionalPing;
|
||||
float zoomT = zoom, pingDirectionT = 0.0f;
|
||||
bool mineralScanner = useMineralScanner;
|
||||
if (isActive)
|
||||
{
|
||||
zoomT = msg.ReadRangedSingle(0.0f, 1.0f, 8);
|
||||
@@ -351,6 +357,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
pingDirectionT = msg.ReadRangedSingle(0.0f, 1.0f, 8);
|
||||
}
|
||||
mineralScanner = msg.ReadBoolean();
|
||||
}
|
||||
|
||||
if (!item.CanClientAccess(c)) { return; }
|
||||
@@ -366,9 +373,14 @@ 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;
|
||||
#if CLIENT
|
||||
zoomSlider.BarScroll = zoomT;
|
||||
directionalModeSwitch.Selected = useDirectionalPing;
|
||||
if (mineralScannerSwitch != null)
|
||||
{
|
||||
mineralScannerSwitch.Selected = useMineralScanner;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#if SERVER
|
||||
@@ -388,6 +400,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.Write(useMineralScanner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,10 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (pathFinder == null)
|
||||
{
|
||||
pathFinder = new PathFinder(WayPoint.WayPointList, false);
|
||||
pathFinder = new PathFinder(WayPoint.WayPointList, false)
|
||||
{
|
||||
GetNodePenalty = GetNodePenalty
|
||||
};
|
||||
}
|
||||
MaintainPos = true;
|
||||
if (posToMaintain == null)
|
||||
@@ -87,7 +90,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Editable(0.0f, 1.0f, decimals: 3),
|
||||
[Editable(0.0f, 1.0f, decimals: 4),
|
||||
Serialize(0.5f, true, description: "How full the ballast tanks should be when the submarine is not being steered upwards/downwards."
|
||||
+ " Can be used to compensate if the ballast tanks are too large/small relative to the size of the submarine.")]
|
||||
public float NeutralBallastLevel
|
||||
@@ -417,6 +420,7 @@ namespace Barotrauma.Items.Components
|
||||
Math.Max(1000.0f * Math.Abs(controlledSub.Velocity.Y), controlledSub.Borders.Height * 0.75f));
|
||||
|
||||
float avoidRadius = avoidDist.Length();
|
||||
float damagingWallAvoidRadius = avoidRadius * 1.5f;
|
||||
|
||||
Vector2 newAvoidStrength = Vector2.Zero;
|
||||
|
||||
@@ -426,12 +430,26 @@ namespace Barotrauma.Items.Components
|
||||
var closeCells = Level.Loaded.GetCells(controlledSub.WorldPosition, 4);
|
||||
foreach (VoronoiCell cell in closeCells)
|
||||
{
|
||||
if (cell.DoesDamage)
|
||||
{
|
||||
foreach (GraphEdge edge in cell.Edges)
|
||||
{
|
||||
Vector2 closestPoint = MathUtils.GetClosestPointOnLineSegment(edge.Point1 + cell.Translation, edge.Point2 + cell.Translation, controlledSub.WorldPosition);
|
||||
float dist = Vector2.Distance(closestPoint, controlledSub.WorldPosition);
|
||||
if (dist > damagingWallAvoidRadius) { continue; }
|
||||
Vector2 diff = controlledSub.WorldPosition - cell.Center;
|
||||
Vector2 avoid = Vector2.Normalize(diff) * (damagingWallAvoidRadius - dist) / damagingWallAvoidRadius;
|
||||
newAvoidStrength += avoid;
|
||||
debugDrawObstacles.Add(new ObstacleDebugInfo(edge, edge.Center, 1.0f, avoid, cell.Translation));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (GraphEdge edge in cell.Edges)
|
||||
{
|
||||
if (MathUtils.GetLineIntersection(edge.Point1 + cell.Translation, edge.Point2 + cell.Translation, controlledSub.WorldPosition, cell.Center, out Vector2 intersection))
|
||||
{
|
||||
Vector2 diff = controlledSub.WorldPosition - intersection;
|
||||
|
||||
//far enough -> ignore
|
||||
if (Math.Abs(diff.X) > avoidDist.X && Math.Abs(diff.Y) > avoidDist.Y)
|
||||
{
|
||||
@@ -497,6 +515,15 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
private float? GetNodePenalty(PathNode node, PathNode nextNode)
|
||||
{
|
||||
if (node.Waypoint?.Tunnel == null || controlledSub == null || node.Waypoint.Tunnel.Type == Level.TunnelType.MainPath) { return 0.0f; }
|
||||
//never navigate from the main path to another type of path
|
||||
if (node.Waypoint.Tunnel.Type == Level.TunnelType.MainPath && nextNode.Waypoint?.Tunnel?.Type != Level.TunnelType.MainPath) { return null; }
|
||||
//higher cost for side paths (= autopilot prefers the main path, but can still navigate side paths if it ends up on one)
|
||||
return 1000.0f;
|
||||
}
|
||||
|
||||
private void UpdatePath()
|
||||
{
|
||||
if (Level.Loaded == null) { return; }
|
||||
|
||||
@@ -13,11 +13,7 @@ namespace Barotrauma.Items.Components
|
||||
set { oxygenFlow = Math.Max(value, 0.0f); }
|
||||
}
|
||||
|
||||
public Vent (Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
|
||||
}
|
||||
public Vent (Item item, XElement element) : base(item, element) { }
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
using Barotrauma.Networking;
|
||||
using System.Xml.Linq;
|
||||
#if CLIENT
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
#endif
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
|
||||
@@ -55,6 +55,22 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
private float extraLoad;
|
||||
private float extraLoadSetTime;
|
||||
/// <summary>
|
||||
/// Additional load coming from somewhere else than the devices connected to the junction box (e.g. ballast flora or piezo crystals).
|
||||
/// Goes back to zero automatically if you stop setting the value.
|
||||
/// </summary>
|
||||
public float ExtraLoad
|
||||
{
|
||||
get { return extraLoad; }
|
||||
set
|
||||
{
|
||||
extraLoad = Math.Max(value, 0.0f);
|
||||
extraLoadSetTime = (float)Timing.TotalTime;
|
||||
}
|
||||
}
|
||||
|
||||
//can the component transfer power
|
||||
private bool canTransfer;
|
||||
public bool CanTransfer
|
||||
@@ -135,6 +151,11 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
RefreshConnections();
|
||||
|
||||
if (Timing.TotalTime > extraLoadSetTime + 1.0)
|
||||
{
|
||||
extraLoad = Math.Max(extraLoad - 1000.0f * deltaTime, 0);
|
||||
}
|
||||
|
||||
if (!CanTransfer) { return; }
|
||||
|
||||
if (isBroken)
|
||||
|
||||
@@ -231,8 +231,16 @@ namespace Barotrauma.Items.Components
|
||||
//and send out a "probe signal" which the PowerTransfer components use to add up the grid power/load
|
||||
foreach (Powered powered in poweredList)
|
||||
{
|
||||
if (powered is PowerTransfer) { continue; }
|
||||
if (powered.currPowerConsumption > 0.0f)
|
||||
if (powered is PowerTransfer pt)
|
||||
{
|
||||
if (pt.ExtraLoad > 0.0f)
|
||||
{
|
||||
lastPowerProbeRecipients.Clear();
|
||||
powered.powerIn?.SendPowerProbeSignal(powered.item, -pt.ExtraLoad);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (powered.currPowerConsumption > 0.0f)
|
||||
{
|
||||
//consuming power
|
||||
lastPowerProbeRecipients.Clear();
|
||||
|
||||
@@ -60,14 +60,14 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public List<Body> IgnoredBodies;
|
||||
|
||||
private Character user;
|
||||
private Character _user;
|
||||
public Character User
|
||||
{
|
||||
get { return user; }
|
||||
get { return _user; }
|
||||
set
|
||||
{
|
||||
user = value;
|
||||
Attack?.SetUser(user);
|
||||
_user = value;
|
||||
Attack?.SetUser(_user);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,7 +211,54 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Use(float deltaTime, Character character = null)
|
||||
private void Launch(Character user, Vector2 simPosition, float rotation)
|
||||
{
|
||||
Item.body.ResetDynamics();
|
||||
Item.SetTransform(simPosition, rotation);
|
||||
// Set user for hitscan projectiles to work properly.
|
||||
User = user;
|
||||
// Need to set null for non-characterusable items.
|
||||
Use(character: null);
|
||||
// Set user for normal projectiles to work properly.
|
||||
User = user;
|
||||
if (Item.Removed) { return; }
|
||||
launchPos = simPosition;
|
||||
//set the rotation of the projectile again because dropping the projectile resets the rotation
|
||||
Item.SetTransform(simPosition, rotation + (Item.body.Dir * LaunchRotationRadians));
|
||||
}
|
||||
|
||||
public void Shoot(Character user, Vector2 weaponPos, Vector2 spawnPos, float rotation, List<Body> ignoredBodies, bool createNetworkEvent)
|
||||
{
|
||||
//add the limbs of the shooter to the list of bodies to be ignored
|
||||
//so that the player can't shoot himself
|
||||
IgnoredBodies = ignoredBodies;
|
||||
Vector2 projectilePos = weaponPos;
|
||||
//make sure there's no obstacles between the base of the weapon (or the shoulder of the character) and the end of the barrel
|
||||
if (Submarine.PickBody(weaponPos, spawnPos, IgnoredBodies, Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionItemBlocking) == null)
|
||||
{
|
||||
//no obstacles -> we can spawn the projectile at the barrel
|
||||
projectilePos = spawnPos;
|
||||
}
|
||||
else if ((weaponPos - spawnPos).LengthSquared() > 0.0001f)
|
||||
{
|
||||
//spawn the projectile body.GetMaxExtent() away from the position where the raycast hit the obstacle
|
||||
Vector2 newPos = weaponPos - Vector2.Normalize(spawnPos - projectilePos) * Math.Max(Item.body.GetMaxExtent(), 0.1f);
|
||||
if (MathUtils.IsValid(newPos))
|
||||
{
|
||||
projectilePos = newPos;
|
||||
}
|
||||
}
|
||||
Launch(user, projectilePos, rotation);
|
||||
if (createNetworkEvent && !Item.Removed && GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
#if SERVER
|
||||
launchRot = rotation;
|
||||
Item.CreateServerEvent(this, new object[] { true }); //true = indicate that this is a launch event
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public bool Use(Character character = null)
|
||||
{
|
||||
if (character != null && !characterUsable) { return false; }
|
||||
|
||||
@@ -230,16 +277,16 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
Launch(launchDir * LaunchImpulse * item.body.Mass);
|
||||
DoLaunch(launchDir * LaunchImpulse * item.body.Mass);
|
||||
}
|
||||
}
|
||||
|
||||
User = character;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void Launch(Vector2 impulse)
|
||||
public override bool Use(float deltaTime, Character character = null) => Use(character);
|
||||
|
||||
private void DoLaunch(Vector2 impulse)
|
||||
{
|
||||
hits.Clear();
|
||||
|
||||
@@ -342,7 +389,15 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
Entity.Spawner.AddToRemoveQueue(item);
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)
|
||||
{
|
||||
//clients aren't allowed to remove items by themselves, so lets hide the projectile until the server tells us to remove it
|
||||
item.HiddenInGame = Hitscan;
|
||||
}
|
||||
else
|
||||
{
|
||||
Entity.Spawner.AddToRemoveQueue(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -360,6 +415,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
//ignore sensors and items
|
||||
if (fixture?.Body == null || fixture.IsSensor) { return true; }
|
||||
if (fixture.Body.UserData is VineTile) { return true; }
|
||||
if (fixture.Body.UserData is Item item && (item.GetComponent<Door>() == null && !item.Prefab.DamagedByProjectiles || item.Condition <= 0)) { return true; }
|
||||
if (fixture.Body?.UserData as string == "ruinroom") { return true; }
|
||||
|
||||
@@ -381,6 +437,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
//ignore sensors and items
|
||||
if (fixture?.Body == null || fixture.IsSensor) { return -1; }
|
||||
if (fixture.Body.UserData is VineTile) { return -1; }
|
||||
|
||||
if (fixture.Body.UserData is Item item && (item.GetComponent<Door>() == null && !item.Prefab.DamagedByProjectiles || item.Condition <= 0)) { return -1; }
|
||||
if (fixture.Body?.UserData as string == "ruinroom") { return -1; }
|
||||
@@ -577,20 +634,27 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (Attack != null) { attackResult = Attack.DoDamage(User, damageable, item.WorldPosition, 1.0f); }
|
||||
}
|
||||
else if (target.Body.UserData is VoronoiCell voronoiCell && voronoiCell.IsDestructible && Attack != null && Math.Abs(Attack.StructureDamage) > 0.0f)
|
||||
{
|
||||
if (Level.Loaded?.ExtraWalls.Find(w => w.Body == target.Body) is DestructibleLevelWall destructibleWall)
|
||||
{
|
||||
attackResult = Attack.DoDamage(User, destructibleWall, item.WorldPosition, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
if (character != null) { character.LastDamageSource = item; }
|
||||
|
||||
#if CLIENT
|
||||
PlaySound(ActionType.OnUse, user: user);
|
||||
PlaySound(ActionType.OnImpact, user: user);
|
||||
PlaySound(ActionType.OnUse, user: _user);
|
||||
PlaySound(ActionType.OnImpact, user: _user);
|
||||
#endif
|
||||
|
||||
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
if (target.Body.UserData is Limb targetLimb)
|
||||
{
|
||||
ApplyStatusEffects(ActionType.OnUse, 1.0f, character, targetLimb, user: user);
|
||||
ApplyStatusEffects(ActionType.OnImpact, 1.0f, character, targetLimb, user: user);
|
||||
ApplyStatusEffects(ActionType.OnUse, 1.0f, character, targetLimb, user: _user);
|
||||
ApplyStatusEffects(ActionType.OnImpact, 1.0f, character, targetLimb, user: _user);
|
||||
var attack = targetLimb.attack;
|
||||
if (attack != null)
|
||||
{
|
||||
@@ -626,8 +690,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
ApplyStatusEffects(ActionType.OnUse, 1.0f, useTarget: target.Body.UserData as Entity, user: user);
|
||||
ApplyStatusEffects(ActionType.OnImpact, 1.0f, useTarget: target.Body.UserData as Entity, user: user);
|
||||
ApplyStatusEffects(ActionType.OnUse, 1.0f, useTarget: target.Body.UserData as Entity, user: _user);
|
||||
ApplyStatusEffects(ActionType.OnImpact, 1.0f, useTarget: target.Body.UserData as Entity, user: _user);
|
||||
#if SERVER
|
||||
if (GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
@@ -703,7 +767,15 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (RemoveOnHit)
|
||||
{
|
||||
Entity.Spawner.AddToRemoveQueue(item);
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)
|
||||
{
|
||||
//clients aren't allowed to remove items by themselves, so lets hide the projectile until the server tells us to remove it
|
||||
item.HiddenInGame = Hitscan;
|
||||
}
|
||||
else
|
||||
{
|
||||
Entity.Spawner?.AddToRemoveQueue(item);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -446,7 +446,7 @@ namespace Barotrauma.Items.Components
|
||||
//oxygen generators don't deteriorate if they're not running
|
||||
if (oxyGenerator.CurrFlow > 0.1f) { return true; }
|
||||
}
|
||||
else if (ic is Powered powered)
|
||||
else if (ic is Powered powered && !(powered is LightComponent))
|
||||
{
|
||||
if (powered.Voltage >= powered.MinVoltage) { return true; }
|
||||
}
|
||||
@@ -477,6 +477,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private void UpdateFixAnimation(Character character)
|
||||
{
|
||||
if (character == null || character.IsDead || character.IsIncapacitated) { return; }
|
||||
character.AnimController.UpdateUseItem(false, item.WorldPosition + new Vector2(0.0f, 100.0f) * ((item.Condition / item.MaxCondition) % 0.1f));
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
get
|
||||
{
|
||||
if (recipientsDirty) RefreshRecipients();
|
||||
if (recipientsDirty) { RefreshRecipients(); }
|
||||
return recipients;
|
||||
}
|
||||
}
|
||||
@@ -61,7 +61,7 @@ namespace Barotrauma.Items.Components
|
||||
return "Connection (" + item.Name + ", " + Name + ")";
|
||||
}
|
||||
|
||||
public Connection(XElement element, ConnectionPanel connectionPanel)
|
||||
public Connection(XElement element, ConnectionPanel connectionPanel, IdRemap idRemap)
|
||||
{
|
||||
|
||||
#if CLIENT
|
||||
@@ -150,8 +150,11 @@ namespace Barotrauma.Items.Components
|
||||
if (index == -1) break;
|
||||
|
||||
int id = subElement.GetAttributeInt("w", 0);
|
||||
if (id < 0) id = 0;
|
||||
wireId[index] = (ushort)id;
|
||||
if (id < 0)
|
||||
{
|
||||
id = 0;
|
||||
}
|
||||
wireId[index] = idRemap.GetOffsetId(id);
|
||||
|
||||
break;
|
||||
|
||||
@@ -162,6 +165,11 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void SetRecipientsDirty()
|
||||
{
|
||||
recipientsDirty = true;
|
||||
}
|
||||
|
||||
private void RefreshRecipients()
|
||||
{
|
||||
recipients.Clear();
|
||||
@@ -304,6 +312,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (wires[i].Item.body != null) wires[i].Item.body.Enabled = false;
|
||||
wires[i].Connect(this, false, false);
|
||||
wires[i].FixNodeEnds();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,10 +50,10 @@ namespace Barotrauma.Items.Components
|
||||
switch (subElement.Name.ToString())
|
||||
{
|
||||
case "input":
|
||||
Connections.Add(new Connection(subElement, this));
|
||||
Connections.Add(new Connection(subElement, this, IdRemap.DiscardId));
|
||||
break;
|
||||
case "output":
|
||||
Connections.Add(new Connection(subElement, this));
|
||||
Connections.Add(new Connection(subElement, this, IdRemap.DiscardId));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -218,9 +218,9 @@ namespace Barotrauma.Items.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void Load(XElement element, bool usePrefabValues)
|
||||
public override void Load(XElement element, bool usePrefabValues, IdRemap idRemap)
|
||||
{
|
||||
base.Load(element, usePrefabValues);
|
||||
base.Load(element, usePrefabValues, idRemap);
|
||||
|
||||
List<Connection> loadedConnections = new List<Connection>();
|
||||
|
||||
@@ -229,10 +229,10 @@ namespace Barotrauma.Items.Components
|
||||
switch (subElement.Name.ToString())
|
||||
{
|
||||
case "input":
|
||||
loadedConnections.Add(new Connection(subElement, this));
|
||||
loadedConnections.Add(new Connection(subElement, this, idRemap));
|
||||
break;
|
||||
case "output":
|
||||
loadedConnections.Add(new Connection(subElement, this));
|
||||
loadedConnections.Add(new Connection(subElement, this, idRemap));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+49
-21
@@ -14,12 +14,12 @@ namespace Barotrauma.Items.Components
|
||||
private Color lightColor;
|
||||
private float lightBrightness;
|
||||
private float blinkFrequency;
|
||||
private float pulseFrequency, pulseAmount;
|
||||
private float range;
|
||||
private float flicker, flickerState;
|
||||
private float flicker, flickerSpeed;
|
||||
private bool castShadows;
|
||||
private bool drawBehindSubs;
|
||||
|
||||
private float blinkTimer;
|
||||
|
||||
private double lastToggleSignalTime;
|
||||
|
||||
@@ -90,14 +90,49 @@ namespace Barotrauma.Items.Components
|
||||
set
|
||||
{
|
||||
flicker = MathHelper.Clamp(value, 0.0f, 1.0f);
|
||||
#if CLIENT
|
||||
if (light != null) { light.LightSourceParams.Flicker = flicker; }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize(1.0f, false, description: "How fast the light flickers.")]
|
||||
public float FlickerSpeed
|
||||
{
|
||||
get;
|
||||
set;
|
||||
get { return flickerSpeed; }
|
||||
set
|
||||
{
|
||||
flickerSpeed = value;
|
||||
#if CLIENT
|
||||
if (light != null) { light.LightSourceParams.FlickerSpeed = flickerSpeed; }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize(0.0f, true, description: "How rapidly the light pulsates (in Hz). 0 = no blinking.")]
|
||||
public float PulseFrequency
|
||||
{
|
||||
get { return pulseFrequency; }
|
||||
set
|
||||
{
|
||||
pulseFrequency = MathHelper.Clamp(value, 0.0f, 60.0f);
|
||||
#if CLIENT
|
||||
if (light != null) { light.LightSourceParams.PulseFrequency = pulseFrequency; }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
[Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f, DecimalCount = 2), Serialize(0.0f, true, description: "How much light pulsates (in Hz). 0 = not at all, 1 = alternates between full brightness and off.")]
|
||||
public float PulseAmount
|
||||
{
|
||||
get { return pulseAmount; }
|
||||
set
|
||||
{
|
||||
pulseAmount = MathHelper.Clamp(value, 0.0f, 1.0f);
|
||||
#if CLIENT
|
||||
if (light != null) { light.LightSourceParams.PulseAmount = pulseAmount; }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize(0.0f, true, description: "How rapidly the light blinks on and off (in Hz). 0 = no blinking.")]
|
||||
@@ -107,6 +142,9 @@ namespace Barotrauma.Items.Components
|
||||
set
|
||||
{
|
||||
blinkFrequency = MathHelper.Clamp(value, 0.0f, 60.0f);
|
||||
#if CLIENT
|
||||
if (light != null) { light.LightSourceParams.BlinkFrequency = blinkFrequency; }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,11 +199,16 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
ParentSub = item.CurrentHull?.Submarine,
|
||||
Position = item.Position,
|
||||
CastShadows = castShadows,
|
||||
CastShadows = castShadows,
|
||||
IsBackground = drawBehindSubs,
|
||||
SpriteScale = Vector2.One * item.Scale,
|
||||
Range = range
|
||||
};
|
||||
light.LightSourceParams.Flicker = flicker;
|
||||
light.LightSourceParams.FlickerSpeed = FlickerSpeed;
|
||||
light.LightSourceParams.PulseAmount = pulseAmount;
|
||||
light.LightSourceParams.PulseFrequency = pulseFrequency;
|
||||
light.LightSourceParams.BlinkFrequency = blinkFrequency;
|
||||
#endif
|
||||
|
||||
IsActive = IsOn;
|
||||
@@ -229,22 +272,7 @@ namespace Barotrauma.Items.Components
|
||||
lightBrightness = MathHelper.Lerp(lightBrightness, powerConsumption <= 0.0f ? 1.0f : Math.Min(Voltage, 1.0f), 0.1f);
|
||||
}
|
||||
|
||||
if (blinkFrequency > 0.0f)
|
||||
{
|
||||
blinkTimer = (blinkTimer + deltaTime * blinkFrequency) % 1.0f;
|
||||
}
|
||||
|
||||
if (blinkTimer > 0.5f)
|
||||
{
|
||||
SetLightSourceState(false, lightBrightness);
|
||||
}
|
||||
else
|
||||
{
|
||||
flickerState += deltaTime * FlickerSpeed;
|
||||
flickerState %= 255;
|
||||
float noise = PerlinNoise.GetPerlin(flickerState, flickerState * 0.5f) * flicker;
|
||||
SetLightSourceState(true, lightBrightness * (1.0f - noise));
|
||||
}
|
||||
SetLightSourceState(true, lightBrightness);
|
||||
|
||||
if (powerIn == null && powerConsumption > 0.0f) { Voltage -= deltaTime; }
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Barotrauma.Networking;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
@@ -7,6 +8,10 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
private const int MaxMessageLength = ChatMessage.MaxLength;
|
||||
|
||||
private const int MaxMessages = 60;
|
||||
|
||||
private List<string> messageHistory = new List<string>(MaxMessages);
|
||||
|
||||
public string DisplayedWelcomeMessage
|
||||
{
|
||||
get;
|
||||
@@ -50,5 +55,41 @@ namespace Barotrauma.Items.Components
|
||||
string inputSignal = signal.Replace("\\n", "\n");
|
||||
ShowOnDisplay(inputSignal);
|
||||
}
|
||||
|
||||
public override void OnItemLoaded()
|
||||
{
|
||||
base.OnItemLoaded();
|
||||
if (!string.IsNullOrEmpty(DisplayedWelcomeMessage))
|
||||
{
|
||||
ShowOnDisplay(DisplayedWelcomeMessage);
|
||||
DisplayedWelcomeMessage = "";
|
||||
//remove welcome message if a game session is running so it doesn't reappear on successive rounds
|
||||
if (GameMain.GameSession != null)
|
||||
{
|
||||
welcomeMessage = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override XElement Save(XElement parentElement)
|
||||
{
|
||||
var componentElement = base.Save(parentElement);
|
||||
for (int i = 0; i < messageHistory.Count; i++)
|
||||
{
|
||||
componentElement.Add(new XAttribute("msg" + i, messageHistory[i]));
|
||||
}
|
||||
return componentElement;
|
||||
}
|
||||
|
||||
public override void Load(XElement componentElement, bool usePrefabValues, IdRemap idRemap)
|
||||
{
|
||||
base.Load(componentElement, usePrefabValues, idRemap);
|
||||
for (int i = 0; i < MaxMessages; i++)
|
||||
{
|
||||
string msg = componentElement.GetAttributeString("msg" + i, null);
|
||||
if (msg == null) { break; }
|
||||
ShowOnDisplay(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+22
-12
@@ -17,7 +17,8 @@ namespace Barotrauma.Items.Components
|
||||
Atan,
|
||||
}
|
||||
|
||||
protected float[] receivedSignal = new float[2];
|
||||
private float[] receivedSignal = new float[2];
|
||||
private float[] timeSinceReceived = new float[2];
|
||||
|
||||
[Serialize(FunctionType.Sin, false, description: "Which kind of function to run the input through.", alwaysUseInstanceValues: true)]
|
||||
public FunctionType Function
|
||||
@@ -41,12 +42,25 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
//reset received signals
|
||||
receivedSignal[0] = float.NaN;
|
||||
receivedSignal[1] = float.NaN;
|
||||
if (Function == FunctionType.Atan)
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
timeSinceReceived[i] += deltaTime;
|
||||
if (timeSinceReceived[i] > 0.1f)
|
||||
{
|
||||
receivedSignal[i] = float.NaN;
|
||||
}
|
||||
}
|
||||
if (!float.IsNaN(receivedSignal[0]) && !float.IsNaN(receivedSignal[1]))
|
||||
{
|
||||
float angle = (float)Math.Atan2(receivedSignal[1], receivedSignal[0]);
|
||||
if (!UseRadians) { angle = MathHelper.ToDegrees(angle); }
|
||||
item.SendSignal(0, angle.ToString("G", CultureInfo.InvariantCulture), "signal_out", null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0, float signalStrength = 1)
|
||||
{
|
||||
float.TryParse(signal, NumberStyles.Float, CultureInfo.InvariantCulture, out float value);
|
||||
@@ -89,17 +103,13 @@ namespace Barotrauma.Items.Components
|
||||
case FunctionType.Atan:
|
||||
if (connection.Name == "signal_in_x")
|
||||
{
|
||||
timeSinceReceived[0] = 0.0f;
|
||||
float.TryParse(signal, NumberStyles.Float, CultureInfo.InvariantCulture, out receivedSignal[0]);
|
||||
}
|
||||
else if (connection.Name == "signal_in_y")
|
||||
{
|
||||
float.TryParse(signal, NumberStyles.Float, CultureInfo.InvariantCulture, out receivedSignal[1]);
|
||||
if (!float.IsNaN(receivedSignal[0]) && !float.IsNaN(receivedSignal[1]))
|
||||
{
|
||||
float angle = (float)Math.Atan2(receivedSignal[1], receivedSignal[0]);
|
||||
if (!UseRadians) { angle = MathHelper.ToDegrees(angle); }
|
||||
item.SendSignal(0, angle.ToString("G", CultureInfo.InvariantCulture), "signal_out", null);
|
||||
}
|
||||
timeSinceReceived[1] = 0.0f;
|
||||
float.TryParse(signal, NumberStyles.Float, CultureInfo.InvariantCulture, out receivedSignal[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -59,6 +61,12 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
item.SendSignal(0, signalOut, "signal_out", null);
|
||||
}
|
||||
|
||||
if (item.CurrentHull != null)
|
||||
{
|
||||
int waterPercentage = MathHelper.Clamp((int)Math.Round(item.CurrentHull.WaterPercentage), 0, 100);
|
||||
item.SendSignal(0, waterPercentage.ToString(), "water_%", null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ namespace Barotrauma.Items.Components
|
||||
channelMemory[index] = MathHelper.Clamp(value, 0, 10000);
|
||||
}
|
||||
|
||||
public void TransmitSignal(int stepsTaken, string signal, Item source, Character sender, bool sendToChat, float signalStrength = 1.0f)
|
||||
public void TransmitSignal(int stepsTaken, string signal, Item source, Character sender, bool sentFromChat, float signalStrength = 1.0f)
|
||||
{
|
||||
var senderComponent = source?.GetComponent<WifiComponent>();
|
||||
if (senderComponent != null && !CanReceive(senderComponent)) { return; }
|
||||
@@ -162,6 +162,8 @@ namespace Barotrauma.Items.Components
|
||||
var receivers = GetReceiversInRange();
|
||||
foreach (WifiComponent wifiComp in receivers)
|
||||
{
|
||||
if (sentFromChat && !wifiComp.LinkToChat) { continue; }
|
||||
|
||||
//signal strength diminishes by distance
|
||||
float sentSignalStrength = signalStrength *
|
||||
MathHelper.Clamp(1.0f - (Vector2.Distance(item.WorldPosition, wifiComp.item.WorldPosition) / wifiComp.range), 0.0f, 1.0f);
|
||||
@@ -176,11 +178,12 @@ namespace Barotrauma.Items.Components
|
||||
source.LastSentSignalRecipients.Add(receiverItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (DiscardDuplicateChatMessages && signal == prevSignal) continue;
|
||||
if (DiscardDuplicateChatMessages && signal == prevSignal) { continue; }
|
||||
|
||||
if (LinkToChat && wifiComp.LinkToChat && chatMsgCooldown <= 0.0f && sendToChat)
|
||||
//create a chat message
|
||||
if (LinkToChat && wifiComp.LinkToChat && chatMsgCooldown <= 0.0f && !sentFromChat)
|
||||
{
|
||||
if (wifiComp.item.ParentInventory != null &&
|
||||
wifiComp.item.ParentInventory.Owner != null)
|
||||
@@ -232,7 +235,7 @@ namespace Barotrauma.Items.Components
|
||||
switch (connection.Name)
|
||||
{
|
||||
case "signal_in":
|
||||
TransmitSignal(stepsTaken, signal, source, sender, true, signalStrength);
|
||||
TransmitSignal(stepsTaken, signal, source, sender, false, signalStrength);
|
||||
break;
|
||||
case "set_channel":
|
||||
if (int.TryParse(signal, out int newChannel))
|
||||
|
||||
@@ -37,6 +37,11 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
angle = MathUtils.VectorToAngle(end - start);
|
||||
length = Vector2.Distance(start, end);
|
||||
|
||||
if (length > 5000.0f)
|
||||
{
|
||||
int akjsdnfkjsadf = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,8 +188,12 @@ namespace Barotrauma.Items.Components
|
||||
if (refSub == null)
|
||||
{
|
||||
Structure attachTarget = Structure.GetAttachTarget(newConnection.Item.WorldPosition);
|
||||
if (attachTarget == null) { continue; }
|
||||
refSub = attachTarget.Submarine;
|
||||
if (attachTarget == null && !(newConnection.Item.GetComponent<Holdable>()?.Attached ?? false))
|
||||
{
|
||||
connections[i] = null;
|
||||
continue;
|
||||
}
|
||||
refSub = attachTarget?.Submarine;
|
||||
}
|
||||
|
||||
Vector2 nodePos = refSub == null ?
|
||||
@@ -200,14 +209,16 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
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], nodePos))
|
||||
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], nodePos))
|
||||
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;
|
||||
}
|
||||
@@ -236,18 +247,18 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
foreach (ItemComponent ic in item.Components)
|
||||
{
|
||||
if (ic == this) continue;
|
||||
if (ic == this) { continue; }
|
||||
ic.Drop(null);
|
||||
}
|
||||
if (item.Container != null) item.Container.RemoveContained(this.item);
|
||||
if (item.body != null) item.body.Enabled = false;
|
||||
if (item.Container != null) { item.Container.RemoveContained(this.item); }
|
||||
if (item.body != null) { item.body.Enabled = false; }
|
||||
|
||||
IsActive = false;
|
||||
|
||||
CleanNodes();
|
||||
}
|
||||
|
||||
if (item.body != null) item.Submarine = newConnection.Item.Submarine;
|
||||
|
||||
if (item.body != null) { item.Submarine = newConnection.Item.Submarine; }
|
||||
|
||||
if (sendNetworkEvent)
|
||||
{
|
||||
@@ -620,8 +631,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (connections[i]?.Item != null)
|
||||
{
|
||||
var pt = connections[i].Item.GetComponent<PowerTransfer>();
|
||||
if (pt != null) pt.SetConnectionDirty(connections[i]);
|
||||
connections[i].Item.GetComponent<PowerTransfer>()?.SetConnectionDirty(connections[i]);
|
||||
connections[i].SetRecipientsDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -651,17 +662,29 @@ namespace Barotrauma.Items.Components
|
||||
} while (removed);
|
||||
}
|
||||
|
||||
private void FixNodeEnds()
|
||||
public void FixNodeEnds()
|
||||
{
|
||||
if (connections[0] == null || connections[1] == null || nodes.Count == 0) { return; }
|
||||
Item item0 = connections[0]?.Item;
|
||||
Item item1 = connections[1]?.Item;
|
||||
|
||||
if (item0 == null && item1 != null)
|
||||
{
|
||||
item0 = Item.ItemList.Find(it => it.GetComponent<ConnectionPanel>()?.DisconnectedWires.Contains(this) ?? false);
|
||||
}
|
||||
else if (item0 != null && item1 == null)
|
||||
{
|
||||
item1 = Item.ItemList.Find(it => it.GetComponent<ConnectionPanel>()?.DisconnectedWires.Contains(this) ?? false);
|
||||
}
|
||||
|
||||
if (item0 == null || item1 == null || nodes.Count == 0) { return; }
|
||||
|
||||
Vector2 nodePos = nodes[0];
|
||||
|
||||
Submarine refSub = connections[0].Item.Submarine ?? connections[1].Item.Submarine;
|
||||
Submarine refSub = item0.Submarine ?? item1.Submarine;
|
||||
if (refSub != null) { nodePos += refSub.HiddenSubPosition; }
|
||||
|
||||
float dist1 = Vector2.DistanceSquared(connections[0].Item.Position, nodePos);
|
||||
float dist2 = Vector2.DistanceSquared(connections[1].Item.Position, nodePos);
|
||||
float dist1 = Vector2.DistanceSquared(item0.Position, nodePos);
|
||||
float dist2 = Vector2.DistanceSquared(item1.Position, nodePos);
|
||||
|
||||
//first node is closer to the second item
|
||||
//= the nodes are "backwards", need to reverse them
|
||||
@@ -721,6 +744,11 @@ namespace Barotrauma.Items.Components
|
||||
public override void FlipX(bool relativeToSub)
|
||||
{
|
||||
if (item.ParentInventory != null) { return; }
|
||||
#if CLIENT
|
||||
if (!relativeToSub && Screen.Selected != GameMain.SubEditorScreen) { return; }
|
||||
#else
|
||||
if (!relativeToSub) { return; }
|
||||
#endif
|
||||
|
||||
Vector2 refPos = item.Submarine == null ?
|
||||
Vector2.Zero :
|
||||
@@ -750,9 +778,9 @@ namespace Barotrauma.Items.Components
|
||||
UpdateSections();
|
||||
}
|
||||
|
||||
public override void Load(XElement componentElement, bool usePrefabValues)
|
||||
public override void Load(XElement componentElement, bool usePrefabValues, IdRemap idRemap)
|
||||
{
|
||||
base.Load(componentElement, usePrefabValues);
|
||||
base.Load(componentElement, usePrefabValues, idRemap);
|
||||
|
||||
string nodeString = componentElement.GetAttributeString("nodes", "");
|
||||
if (nodeString == "") return;
|
||||
|
||||
@@ -76,7 +76,7 @@ namespace Barotrauma.Items.Components
|
||||
set { launchImpulse = value; }
|
||||
}
|
||||
|
||||
[Editable(0.0f, 1000.0f), Serialize(5.0f, false, description: "The period of time the user has to wait between shots.")]
|
||||
[Editable(0.0f, 1000.0f, decimals: 3), Serialize(5.0f, false, description: "The period of time the user has to wait between shots.")]
|
||||
public float Reload
|
||||
{
|
||||
get { return reloadTime; }
|
||||
@@ -198,6 +198,7 @@ namespace Barotrauma.Items.Components
|
||||
private set;
|
||||
}
|
||||
|
||||
private float prevScale;
|
||||
float prevBaseRotation;
|
||||
[Serialize(0.0f, true, description: "The angle of the turret's base in degrees.", alwaysUseInstanceValues: true)]
|
||||
public float BaseRotation
|
||||
@@ -250,20 +251,17 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private void UpdateTransformedBarrelPos()
|
||||
{
|
||||
float flippedRotation = item.Rotation;
|
||||
if (item.FlippedX) { flippedRotation = -flippedRotation; }
|
||||
//if (item.FlippedY) flippedRotation = 180.0f - flippedRotation;
|
||||
transformedBarrelPos = MathUtils.RotatePointAroundTarget(barrelPos * item.Scale, new Vector2(item.Rect.Width / 2, item.Rect.Height / 2), flippedRotation);
|
||||
transformedBarrelPos = MathUtils.RotatePointAroundTarget(barrelPos * item.Scale, new Vector2(item.Rect.Width / 2, item.Rect.Height / 2), item.Rotation);
|
||||
#if CLIENT
|
||||
item.ResetCachedVisibleSize();
|
||||
#endif
|
||||
item.Rotation = flippedRotation;
|
||||
prevBaseRotation = item.Rotation;
|
||||
prevScale = item.Scale;
|
||||
}
|
||||
|
||||
public override void OnItemLoaded()
|
||||
public override void OnMapLoaded()
|
||||
{
|
||||
base.OnItemLoaded();
|
||||
base.OnMapLoaded();
|
||||
var lightComponents = item.GetComponents<LightComponent>();
|
||||
if (lightComponents != null && lightComponents.Count() > 0)
|
||||
{
|
||||
@@ -277,6 +275,9 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (loadedRotationLimits.HasValue) { RotationLimits = loadedRotationLimits.Value; }
|
||||
if (loadedBaseRotation.HasValue) { BaseRotation = loadedBaseRotation.Value; }
|
||||
UpdateTransformedBarrelPos();
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
@@ -284,7 +285,7 @@ namespace Barotrauma.Items.Components
|
||||
this.cam = cam;
|
||||
|
||||
if (reload > 0.0f) { reload -= deltaTime; }
|
||||
if (!MathUtils.NearlyEqual(item.Rotation, prevBaseRotation))
|
||||
if (!MathUtils.NearlyEqual(item.Rotation, prevBaseRotation) || !MathUtils.NearlyEqual(item.Scale, prevScale))
|
||||
{
|
||||
UpdateTransformedBarrelPos();
|
||||
}
|
||||
@@ -328,19 +329,42 @@ namespace Barotrauma.Items.Components
|
||||
user.WorldPosition + Vector2.UnitY * 150.0f);
|
||||
}
|
||||
|
||||
float rotMidDiff = MathHelper.WrapAngle(rotation - (minRotation + maxRotation) / 2.0f);
|
||||
|
||||
float targetRotationDiff = MathHelper.WrapAngle(targetRotation - rotation);
|
||||
|
||||
if ((maxRotation - minRotation) < MathHelper.TwoPi)
|
||||
{
|
||||
float targetRotationMaxDiff = MathHelper.WrapAngle(targetRotation - maxRotation);
|
||||
float targetRotationMinDiff = MathHelper.WrapAngle(targetRotation - minRotation);
|
||||
|
||||
if (Math.Abs(targetRotationMaxDiff) < Math.Abs(targetRotationMinDiff) &&
|
||||
rotMidDiff < 0.0f &&
|
||||
targetRotationDiff < 0.0f)
|
||||
{
|
||||
targetRotationDiff += MathHelper.TwoPi;
|
||||
}
|
||||
else if (Math.Abs(targetRotationMaxDiff) > Math.Abs(targetRotationMinDiff) &&
|
||||
rotMidDiff > 0.0f &&
|
||||
targetRotationDiff > 0.0f)
|
||||
{
|
||||
targetRotationDiff -= MathHelper.TwoPi;
|
||||
}
|
||||
}
|
||||
|
||||
angularVelocity +=
|
||||
(MathHelper.WrapAngle(targetRotation - rotation) * springStiffness - angularVelocity * springDamping) * deltaTime;
|
||||
(targetRotationDiff * springStiffness - angularVelocity * springDamping) * deltaTime;
|
||||
angularVelocity = MathHelper.Clamp(angularVelocity, -rotationSpeed, rotationSpeed);
|
||||
|
||||
rotation += angularVelocity * deltaTime;
|
||||
|
||||
float rotMidDiff = MathHelper.WrapAngle(rotation - (minRotation + maxRotation) / 2.0f);
|
||||
rotMidDiff = MathHelper.WrapAngle(rotation - (minRotation + maxRotation) / 2.0f);
|
||||
|
||||
if (rotMidDiff < -maxDist)
|
||||
{
|
||||
rotation = minRotation;
|
||||
angularVelocity *= -0.5f;
|
||||
}
|
||||
}
|
||||
else if (rotMidDiff > maxDist)
|
||||
{
|
||||
rotation = maxRotation;
|
||||
@@ -804,7 +828,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
if (objective.SubObjectives.None())
|
||||
{
|
||||
var loadItemsObjective = AIContainItems<Turret>(container, character, objective, usableProjectileCount + 1, equip: true, removeEmpty: true);
|
||||
var loadItemsObjective = AIContainItems<Turret>(container, character, objective, usableProjectileCount + 1, equip: true, removeEmpty: true, dropItemOnDeselected: true);
|
||||
if (loadItemsObjective == null)
|
||||
{
|
||||
if (usableProjectileCount == 0)
|
||||
@@ -976,6 +1000,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void FlipX(bool relativeToSub)
|
||||
{
|
||||
BaseRotation = MathHelper.ToDegrees(MathUtils.WrapAngleTwoPi(MathHelper.ToRadians(-BaseRotation)));
|
||||
|
||||
minRotation = MathHelper.Pi - minRotation;
|
||||
maxRotation = MathHelper.Pi - maxRotation;
|
||||
|
||||
@@ -997,7 +1023,22 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void FlipY(bool relativeToSub)
|
||||
{
|
||||
BaseRotation = MathHelper.ToDegrees(MathUtils.WrapAngleTwoPi(MathHelper.ToRadians(BaseRotation - 180)));
|
||||
BaseRotation = MathHelper.ToDegrees(MathUtils.WrapAngleTwoPi(MathHelper.ToRadians(180 - BaseRotation)));
|
||||
|
||||
minRotation = -minRotation;
|
||||
maxRotation = -maxRotation;
|
||||
|
||||
var temp = minRotation;
|
||||
minRotation = maxRotation;
|
||||
maxRotation = temp;
|
||||
|
||||
while (minRotation < 0)
|
||||
{
|
||||
minRotation += MathHelper.TwoPi;
|
||||
maxRotation += MathHelper.TwoPi;
|
||||
}
|
||||
rotation = (minRotation + maxRotation) / 2;
|
||||
|
||||
UpdateTransformedBarrelPos();
|
||||
}
|
||||
|
||||
@@ -1041,6 +1082,25 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
private Vector2? loadedRotationLimits;
|
||||
private float? loadedBaseRotation;
|
||||
public override void Load(XElement componentElement, bool usePrefabValues, IdRemap idRemap)
|
||||
{
|
||||
base.Load(componentElement, usePrefabValues, idRemap);
|
||||
loadedRotationLimits = componentElement.GetAttributeVector2("rotationlimits", RotationLimits);
|
||||
loadedBaseRotation = componentElement.GetAttributeFloat("baserotation", componentElement.Parent.GetAttributeFloat("rotation", BaseRotation));
|
||||
}
|
||||
|
||||
public override void OnItemLoaded()
|
||||
{
|
||||
base.OnItemLoaded();
|
||||
if (!loadedBaseRotation.HasValue)
|
||||
{
|
||||
if (item.FlippedX) { FlipX(relativeToSub: false); }
|
||||
if (item.FlippedY) { FlipY(relativeToSub: false); }
|
||||
}
|
||||
}
|
||||
|
||||
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
|
||||
{
|
||||
if (extraData.Length > 2)
|
||||
|
||||
@@ -441,9 +441,9 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
private int loadedVariant = -1;
|
||||
public override void Load(XElement componentElement, bool usePrefabValues)
|
||||
public override void Load(XElement componentElement, bool usePrefabValues, IdRemap idRemap)
|
||||
{
|
||||
base.Load(componentElement, usePrefabValues);
|
||||
base.Load(componentElement, usePrefabValues, idRemap);
|
||||
loadedVariant = componentElement.GetAttributeInt("variant", -1);
|
||||
}
|
||||
public override void OnItemLoaded()
|
||||
|
||||
Reference in New Issue
Block a user