2f107db...5202af9

This commit is contained in:
Joonas Rikkonen
2019-03-18 21:42:26 +02:00
parent 58c92888b7
commit 044fd3344b
395 changed files with 27417 additions and 19754 deletions
@@ -167,6 +167,8 @@ namespace Barotrauma.Items.Components
DockingTarget = null;
return;
}
target.InitializeLinks();
#if CLIENT
PlaySound(ActionType.OnUse, item.WorldPosition);
@@ -209,15 +211,19 @@ namespace Barotrauma.Items.Components
CreateJoint(false);
#if SERVER
if (GameMain.Server != null)
{
item.CreateServerEvent(this);
}
#endif
}
public void Lock(bool isNetworkMessage, bool forcePosition = false)
{
#if CLIENT
if (GameMain.Client != null && !isNetworkMessage) return;
#endif
if (DockingTarget == null)
{
@@ -249,10 +255,12 @@ namespace Barotrauma.Items.Components
ConnectWireBetweenPorts();
CreateJoint(true);
#if SERVER
if (GameMain.Server != null)
{
item.CreateServerEvent(this);
}
#endif
}
@@ -730,10 +738,12 @@ namespace Barotrauma.Items.Components
bodies = null;
}
#if SERVER
if (GameMain.Server != null)
{
item.CreateServerEvent(this);
}
#endif
}
public override void Update(float deltaTime, Camera cam)
@@ -819,8 +829,13 @@ namespace Barotrauma.Items.Components
gap?.Remove(); gap = null;
}
public override void OnMapLoaded()
private bool initialized = false;
private void InitializeLinks()
{
if (initialized) { return; }
initialized = true;
float closestDist = 30.0f * 30.0f;
foreach (Item it in Item.ItemList)
{
if (it.Submarine != item.Submarine) continue;
@@ -828,10 +843,11 @@ namespace Barotrauma.Items.Components
var doorComponent = it.GetComponent<Door>();
if (doorComponent == null) continue;
if (Vector2.Distance(item.Position, doorComponent.Item.Position) < Submarine.GridSize.X)
float distSqr = Vector2.Distance(item.Position, it.Position);
if (distSqr < closestDist)
{
this.door = doorComponent;
break;
door = doorComponent;
closestDist = distSqr;
}
}
@@ -852,9 +868,20 @@ namespace Barotrauma.Items.Components
gap.Remove();
continue;
}
}
}
public override void OnMapLoaded()
{
InitializeLinks();
if (!item.linkedTo.Any()) return;
List<MapEntity> linked = new List<MapEntity>(item.linkedTo);
foreach (MapEntity entity in linked)
{
Item linkedItem = entity as Item;
if (linkedItem == null) continue;
if (linkedItem == null) { continue; }
var dockingPort = linkedItem.GetComponent<DockingPort>();
if (dockingPort != null)
@@ -866,7 +893,9 @@ 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 CLIENT
if (GameMain.Client != null) return;
#endif
bool wasDocked = docked;
DockingPort prevDockingTarget = DockingTarget;
@@ -882,6 +911,7 @@ namespace Barotrauma.Items.Components
break;
}
#if SERVER
if (sender != null && docked != wasDocked)
{
if (docked)
@@ -895,6 +925,7 @@ namespace Barotrauma.Items.Components
GameServer.Log(sender.LogName + " undocked " + item.Submarine.Name + " from " + prevDockingTarget.item.Submarine.Name, ServerLog.MessageType.ItemInteraction);
}
}
#endif
}
public void ServerWrite(Lidgren.Network.NetBuffer msg, Client c, object[] extraData = null)
@@ -16,35 +16,24 @@ namespace Barotrauma.Items.Components
partial class Door : Pickable, IDrawableComponent, IServerSerializable
{
private Gap linkedGap;
private Rectangle window;
private bool isOpen;
private float openState;
private PhysicsBody body;
private Sprite doorSprite, weldedSprite, brokenSprite;
private bool scaleBrokenSprite, fadeBrokenSprite;
private bool isHorizontal;
private bool createdNewGap;
private bool autoOrientGap;
private bool createdNewGap;
private bool autoOrientGap;
private bool isStuck;
private bool? predictedState;
private float resetPredictionTimer;
private Rectangle doorRect;
private bool isBroken;
//openState when the vertices of the convex hull were last calculated
private float lastConvexHullState;
public bool IsBroken
{
get { return isBroken; }
@@ -63,10 +52,7 @@ namespace Barotrauma.Items.Components
}
}
public PhysicsBody Body
{
get { return body; }
}
public PhysicsBody Body { get; private set; }
private float stuck;
[Serialize(0.0f, false)]
@@ -82,10 +68,7 @@ namespace Barotrauma.Items.Components
}
}
public bool? PredictedState
{
get { return predictedState; }
}
public bool? PredictedState { get; private set; }
public Gap LinkedGap
{
@@ -98,12 +81,12 @@ namespace Barotrauma.Items.Components
linkedGap = e as Gap;
if (linkedGap != null)
{
linkedGap.PassAmbientLight = window != Rectangle.Empty;
linkedGap.PassAmbientLight = Window != Rectangle.Empty;
return linkedGap;
}
}
Rectangle rect = item.Rect;
if (isHorizontal)
if (IsHorizontal)
{
rect.Y += 5;
rect.Height += 10;
@@ -114,10 +97,10 @@ namespace Barotrauma.Items.Components
rect.Width += 10;
}
linkedGap = new Gap(rect, !isHorizontal, Item.Submarine)
linkedGap = new Gap(rect, !IsHorizontal, Item.Submarine)
{
Submarine = item.Submarine,
PassAmbientLight = window != Rectangle.Empty,
PassAmbientLight = Window != Rectangle.Empty,
Open = openState
};
item.linkedTo.Add(linkedGap);
@@ -126,18 +109,11 @@ namespace Barotrauma.Items.Components
}
}
public bool IsHorizontal
{
get { return isHorizontal; }
}
public bool IsHorizontal { get; private set; }
[Serialize("0.0,0.0,0.0,0.0", false)]
public Rectangle Window
{
get { return window; }
set { window = value; }
}
public Rectangle Window { get; set; }
[Editable, Serialize(false, true)]
public bool IsOpen
{
@@ -156,7 +132,7 @@ namespace Barotrauma.Items.Components
{
openState = MathHelper.Clamp(value, 0.0f, 1.0f);
#if CLIENT
float size = isHorizontal ? item.Rect.Width : item.Rect.Height;
float size = IsHorizontal ? item.Rect.Width : item.Rect.Height;
if (Math.Abs(lastConvexHullState - openState) * size < 5.0f) { return; }
UpdateConvexHulls();
lastConvexHullState = openState;
@@ -174,7 +150,7 @@ namespace Barotrauma.Items.Components
public Door(Item item, XElement element)
: base(item, element)
{
isHorizontal = element.GetAttributeBool("horizontal", false);
IsHorizontal = element.GetAttributeBool("horizontal", false);
canBePicked = element.GetAttributeBool("canbepicked", false);
autoOrientGap = element.GetAttributeBool("autoorientgap", false);
@@ -203,7 +179,7 @@ namespace Barotrauma.Items.Components
(int)(doorSprite.size.X * item.Scale),
(int)(doorSprite.size.Y * item.Scale));
body = new PhysicsBody(
Body = new PhysicsBody(
ConvertUnits.ToSimUnits(Math.Max(doorRect.Width, 1)),
ConvertUnits.ToSimUnits(Math.Max(doorRect.Height, 1)),
0.0f,
@@ -214,7 +190,7 @@ namespace Barotrauma.Items.Components
BodyType = BodyType.Static,
Friction = 0.5f
};
body.SetTransform(
Body.SetTransform(
ConvertUnits.ToSimUnits(new Vector2(doorRect.Center.X, doorRect.Y - doorRect.Height / 2)),
0.0f);
@@ -225,7 +201,7 @@ namespace Barotrauma.Items.Components
{
base.Move(amount);
body?.SetTransform(body.SimPosition + ConvertUnits.ToSimUnits(amount), 0.0f);
Body?.SetTransform(Body.SimPosition + ConvertUnits.ToSimUnits(amount), 0.0f);
#if CLIENT
UpdateConvexHulls();
@@ -249,7 +225,7 @@ namespace Barotrauma.Items.Components
{
if (item.Condition <= 0.0f) return true; //repairs
SetState(predictedState == null ? !isOpen : !predictedState.Value, false, true); //crowbar function
SetState(PredictedState == null ? !isOpen : !PredictedState.Value, false, true); //crowbar function
#if CLIENT
PlaySound(ActionType.OnPicked, item.WorldPosition, picker);
#endif
@@ -267,7 +243,7 @@ namespace Barotrauma.Items.Components
if (isBroken)
{
//the door has to be restored to 50% health before collision detection on the body is re-enabled
if (item.Condition > 50.0f)
if (item.Condition > item.Prefab.Health / 2.0f)
{
IsBroken = false;
}
@@ -277,20 +253,20 @@ namespace Barotrauma.Items.Components
bool isClosing = false;
if (!isStuck)
{
if (predictedState == null)
if (PredictedState == null)
{
OpenState += deltaTime * (isOpen ? 2.0f : -2.0f);
isClosing = openState > 0.0f && openState < 1.0f && !isOpen;
}
else
{
OpenState += deltaTime * ((bool)predictedState ? 2.0f : -2.0f);
isClosing = openState > 0.0f && openState < 1.0f && !(bool)predictedState;
OpenState += deltaTime * ((bool)PredictedState ? 2.0f : -2.0f);
isClosing = openState > 0.0f && openState < 1.0f && !(bool)PredictedState;
resetPredictionTimer -= deltaTime;
if (resetPredictionTimer <= 0.0f)
{
predictedState = null;
PredictedState = null;
}
}
@@ -303,7 +279,7 @@ namespace Barotrauma.Items.Components
}
else
{
body.Enabled = Impassable || openState < 1.0f;
Body.Enabled = Impassable || openState < 1.0f;
}
//don't use the predicted state here, because it might set
@@ -320,7 +296,7 @@ namespace Barotrauma.Items.Components
{
if (!Impassable)
{
body.FarseerBody.IsSensor = false;
Body.FarseerBody.IsSensor = false;
}
#if CLIENT
UpdateConvexHulls();
@@ -334,7 +310,7 @@ namespace Barotrauma.Items.Components
//because otherwise repairtool raycasts won't hit it
if (!Impassable)
{
body.FarseerBody.IsSensor = true;
Body.FarseerBody.IsSensor = true;
}
linkedGap.Open = 1.0f;
IsOpen = false;
@@ -354,7 +330,7 @@ namespace Barotrauma.Items.Components
Vector2[] corners = GetConvexHullCorners(Rectangle.Empty);
convexHull = new ConvexHull(corners, Color.Black, item);
if (window != Rectangle.Empty) convexHull2 = new ConvexHull(corners, Color.Black, item);
if (Window != Rectangle.Empty) convexHull2 = new ConvexHull(corners, Color.Black, item);
UpdateConvexHulls();
#endif
@@ -364,10 +340,10 @@ namespace Barotrauma.Items.Components
{
base.RemoveComponentSpecific();
if (body != null)
if (Body != null)
{
body.Remove();
body = null;
Body.Remove();
Body = null;
}
//no need to remove the gap if we're unloading the whole submarine
@@ -399,7 +375,7 @@ namespace Barotrauma.Items.Components
//push characters out of the doorway when the door is closing/opening
Vector2 simPos = ConvertUnits.ToSimUnits(new Vector2(item.Rect.X, item.Rect.Y));
Vector2 currSize = isHorizontal ?
Vector2 currSize = IsHorizontal ?
new Vector2(item.Rect.Width * (1.0f - openState), doorSprite.size.Y * item.Scale) :
new Vector2(doorSprite.size.X * item.Scale, item.Rect.Height * (1.0f - openState));
@@ -417,7 +393,7 @@ namespace Barotrauma.Items.Components
" Remoteplayer: " + c.IsRemotePlayer);
continue;
}
int dir = isHorizontal ? Math.Sign(c.SimPosition.Y - item.SimPosition.Y) : Math.Sign(c.SimPosition.X - item.SimPosition.X);
int dir = IsHorizontal ? Math.Sign(c.SimPosition.Y - item.SimPosition.Y) : Math.Sign(c.SimPosition.X - item.SimPosition.X);
List<PhysicsBody> bodies = c.AnimController.Limbs.Select(l => l.body).ToList();
bodies.Add(c.AnimController.Collider);
@@ -435,7 +411,7 @@ namespace Barotrauma.Items.Components
continue;
}
if (isHorizontal)
if (IsHorizontal)
{
if (body.SimPosition.X < simPos.X || body.SimPosition.X > simPos.X + simSize.X) continue;
diff = body.SimPosition.Y - item.SimPosition.Y;
@@ -452,7 +428,7 @@ namespace Barotrauma.Items.Components
SoundPlayer.PlayDamageSound("LimbBlunt", 1.0f, body);
#endif
if (isHorizontal)
if (IsHorizontal)
{
body.SetTransform(new Vector2(body.SimPosition.X, item.SimPosition.Y + dir * simSize.Y * 2.0f), body.Rotation);
body.ApplyLinearImpulse(new Vector2(isOpen ? 0.0f : 1.0f, dir * 2.0f));
@@ -464,7 +440,7 @@ namespace Barotrauma.Items.Components
}
}
if (isHorizontal)
if (IsHorizontal)
{
if (Math.Abs(body.SimPosition.Y - item.SimPosition.Y) > simSize.Y * 0.5f) continue;
@@ -486,7 +462,7 @@ namespace Barotrauma.Items.Components
{
if (isStuck) return;
bool wasOpen = predictedState == null ? isOpen : predictedState.Value;
bool wasOpen = PredictedState == null ? isOpen : PredictedState.Value;
if (connection.Name == "toggle")
{
@@ -497,66 +473,19 @@ namespace Barotrauma.Items.Components
SetState(signal != "0", false, true);
}
bool newState = predictedState == null ? isOpen : predictedState.Value;
if (sender != null && wasOpen != newState)
#if SERVER
if (sender != null && wasOpen != isOpen)
{
GameServer.Log(sender.LogName + (newState ? " opened " : " closed ") + item.Name, ServerLog.MessageType.ItemInteraction);
GameServer.Log(sender.LogName + (isOpen ? " opened " : " closed ") + item.Name, ServerLog.MessageType.ItemInteraction);
}
}
public void SetState(bool open, bool isNetworkMessage, bool sendNetworkMessage = false)
{
if (isStuck ||
(predictedState == null && isOpen == open) ||
(predictedState != null && isOpen == predictedState.Value && isOpen == open)) return;
if (GameMain.Client != null && !isNetworkMessage)
{
bool stateChanged = open != predictedState;
//clients can "predict" that the door opens/closes when a signal is received
//the prediction will be reset after 1 second, setting the door to a state
//sent by the server, or reverting it back to its old state if no msg from server was received
predictedState = open;
resetPredictionTimer = CorrectionDelay;
#if CLIENT
if (stateChanged) PlaySound(ActionType.OnUse, item.WorldPosition);
#endif
}
else
{
isOpen = open;
#if CLIENT
if (!isNetworkMessage || open != predictedState) PlaySound(ActionType.OnUse, item.WorldPosition);
#endif
}
//opening a partially stuck door makes it less stuck
if (isOpen) stuck = MathHelper.Clamp(stuck - 30.0f, 0.0f, 100.0f);
if (sendNetworkMessage)
{
item.CreateServerEvent(this);
}
}
public override void ServerWrite(Lidgren.Network.NetBuffer msg, Client c, object[] extraData = null)
public void TrySetState(bool open, bool isNetworkMessage, bool sendNetworkMessage = false)
{
base.ServerWrite(msg, c, extraData);
msg.Write(isOpen);
msg.WriteRangedSingle(stuck, 0.0f, 100.0f, 8);
SetState(open, isNetworkMessage, sendNetworkMessage);
}
public override void ClientRead(ServerNetObject type, Lidgren.Network.NetBuffer msg, float sendingTime)
{
base.ClientRead(type, msg, sendingTime);
SetState(msg.ReadBoolean(), true);
Stuck = msg.ReadRangedSingle(0.0f, 100.0f, 8);
predictedState = null;
}
partial void SetState(bool open, bool isNetworkMessage, bool sendNetworkMessage);
}
}
@@ -288,7 +288,9 @@ namespace Barotrauma.Items.Components
item.body.Enabled = true;
IsActive = true;
#if SERVER
if (!alreadySelected) GameServer.Log(character.LogName + " equipped " + item.Name, ServerLog.MessageType.ItemInteraction);
#endif
}
}
@@ -297,8 +299,9 @@ namespace Barotrauma.Items.Components
if (picker == null) return;
picker.DeselectItem(item);
#if SERVER
GameServer.Log(character.LogName + " unequipped " + item.Name, ServerLog.MessageType.ItemInteraction);
#endif
item.body.Enabled = false;
IsActive = false;
@@ -354,6 +357,7 @@ namespace Barotrauma.Items.Components
{
DeattachFromWall();
#if SERVER
if (GameMain.Server != null && attachable)
{
item.CreateServerEvent(this);
@@ -362,6 +366,7 @@ namespace Barotrauma.Items.Components
GameServer.Log(picker.LogName + " detached " + item.Name + " from a wall", ServerLog.MessageType.ItemInteraction);
}
}
#endif
return true;
}
@@ -426,11 +431,13 @@ namespace Barotrauma.Items.Components
{
if (!character.IsKeyDown(InputType.Aim)) return false;
if (!CanBeAttached()) return false;
#if SERVER
if (GameMain.Server != null)
{
item.CreateServerEvent(this);
GameServer.Log(character.LogName + " attached " + item.Name + " to a wall", ServerLog.MessageType.ItemInteraction);
}
#endif
item.Drop();
}
@@ -471,7 +478,7 @@ namespace Barotrauma.Items.Components
ApplyStatusEffects(ActionType.OnActive, deltaTime, picker);
if (item.body.Dir != picker.AnimController.Dir) Flip(item);
if (item.body.Dir != picker.AnimController.Dir) Flip();
item.Submarine = picker.Submarine;
@@ -507,7 +514,7 @@ namespace Barotrauma.Items.Components
}
}
protected void Flip(Item item)
public void Flip()
{
handlePos[0].X = -handlePos[0].X;
handlePos[1].X = -handlePos[1].X;
@@ -23,7 +23,7 @@ namespace Barotrauma.Items.Components
{
deattachTimer = Math.Max(0.0f, value);
//clients don't deattach the item until the server says so (handled in ClientRead)
if (GameMain.Client == null && deattachTimer >= DeattachDuration)
if ((GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient) && deattachTimer >= DeattachDuration)
{
holdable.DeattachFromWall();
}
@@ -80,6 +80,7 @@ namespace Barotrauma.Items.Components
trigger.FarseerBody.IsSensor = true;
trigger.FarseerBody.IsStatic = true;
trigger.FarseerBody.CollisionCategories = Physics.CollisionWall;
trigger.FarseerBody.CollidesWith = Physics.CollisionNone;
}
}
@@ -140,7 +140,7 @@ namespace Barotrauma.Items.Components
ApplyStatusEffects(ActionType.OnActive, deltaTime, picker);
if (item.body.Dir != picker.AnimController.Dir) Flip(item);
if (item.body.Dir != picker.AnimController.Dir) Flip();
AnimController ac = picker.AnimController;
@@ -294,8 +294,9 @@ namespace Barotrauma.Items.Components
}
}
if (GameMain.Client != null) return true;
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) return true;
#if SERVER
if (GameMain.Server != null && targetCharacter != null) //TODO: Log structure hits
{
@@ -308,13 +309,14 @@ namespace Barotrauma.Items.Components
});
string logStr = picker?.LogName + " used " + item.Name;
if (item.ContainedItems != null && item.ContainedItems.Length > 0)
if (item.ContainedItems != null && item.ContainedItems.Any())
{
logStr += "(" + string.Join(", ", item.ContainedItems.Select(i => i?.Name)) + ")";
logStr += " (" + string.Join(", ", item.ContainedItems.Select(i => i?.Name)) + ")";
}
logStr += " on " + targetCharacter.LogName + ".";
Networking.GameServer.Log(logStr, Networking.ServerLog.MessageType.Attack);
}
#endif
if (targetCharacter != null) //TODO: Allow OnUse to happen on structures too maybe??
{
@@ -66,7 +66,9 @@ namespace Barotrauma.Items.Components
{
if (picker.PickingItem == null && PickingTime <= float.MaxValue)
{
#if SERVER
item.CreateServerEvent(this);
#endif
CoroutineManager.StartCoroutine(WaitForPick(picker, PickingTime));
}
return false;
@@ -143,7 +145,11 @@ namespace Barotrauma.Items.Components
StopPicking(picker);
if (!picker.IsRemotePlayer || GameMain.Server != null) OnPicked(picker);
bool isNotRemote = true;
#if CLIENT
isNotRemote = !picker.IsRemotePlayer;
#endif
if (isNotRemote) OnPicked(picker);
yield return CoroutineStatus.Success;
}
@@ -92,7 +92,7 @@ namespace Barotrauma.Items.Components
}
Projectile projectile = null;
Item[] containedItems = item.ContainedItems;
var containedItems = item.ContainedItems;
if (containedItems == null) return true;
foreach (Item item in containedItems)
@@ -105,18 +105,29 @@ namespace Barotrauma.Items.Components
{
foreach (Item item in containedItems)
{
Item[] containedSubItems = item.ContainedItems;
foreach (Item subItem in containedSubItems)
projectile = item.GetComponent<Projectile>();
if (projectile != null) break;
}
//projectile not found, see if one of the contained items contains projectiles
if (projectile == null)
{
foreach (Item item in containedItems)
{
projectile = subItem.GetComponent<Projectile>();
//apply OnUse statuseffects to the container in case it has to react to it somehow
//(play a sound, spawn more projectiles, reduce condition...)
subItem.GetComponent<ItemContainer>()?.Item.ApplyStatusEffects(ActionType.OnUse, deltaTime);
if (projectile != null) break;
var containedSubItems = item.ContainedItems;
if (containedSubItems == null) { continue; }
foreach (Item subItem in containedSubItems)
{
projectile = subItem.GetComponent<Projectile>();
//apply OnUse statuseffects to the container in case it has to react to it somehow
//(play a sound, spawn more projectiles, reduce condition...)
subItem.GetComponent<ItemContainer>()?.Item.ApplyStatusEffects(ActionType.OnUse, deltaTime);
if (projectile != null) break;
}
}
}
}
if (projectile == null) return true;
float spread = MathHelper.ToRadians(MathHelper.Lerp(Spread, UnskilledSpread, degreeOfFailure));
@@ -14,52 +14,35 @@ namespace Barotrauma.Items.Components
partial class RepairTool : ItemComponent
{
private readonly List<string> fixableEntities;
private float range;
private Vector2 pickedPosition;
private Vector2 barrelPos;
private float activeTimer;
[Serialize(0.0f, false)]
public float Range
{
get { return range; }
set { range = value; }
}
public float Range { get; set; }
[Serialize(0.0f, false)]
public float StructureFixAmount
{
get; set;
}
[Serialize(0.0f, false)]
public float LimbFixAmount
{
get; set;
}
[Serialize(0.0f, false)]
public float ExtinguishAmount
{
get; set;
}
[Serialize("0.0,0.0", false)]
public Vector2 BarrelPos
{
get { return barrelPos; }
set { barrelPos = value; }
}
public Vector2 BarrelPos { get; set; }
[Serialize(false, false)]
public bool RepairThroughWalls { get; set; }
public Vector2 TransformedBarrelPos
{
get
{
Matrix bodyTransform = Matrix.CreateRotationZ(item.body.Rotation);
Vector2 flippedPos = barrelPos;
Vector2 flippedPos = BarrelPos;
if (item.body.Dir < 0.0f) flippedPos.X = -flippedPos.X;
return (Vector2.Transform(flippedPos, bodyTransform));
}
@@ -70,6 +53,11 @@ namespace Barotrauma.Items.Components
{
this.item = item;
if (element.Attribute("limbfixamount") != null)
{
DebugConsole.ThrowError("Error in item \"" + item.Name + "\" - RepairTool damage should be configured using a StatusEffect with Afflictions, not the limbfixamount attribute.");
}
fixableEntities = new List<string>();
foreach (XElement subElement in element.Elements())
{
@@ -116,7 +104,7 @@ namespace Barotrauma.Items.Components
Vector2 targetPosition = item.WorldPosition;
targetPosition += new Vector2(
(float)Math.Cos(item.body.Rotation),
(float)Math.Sin(item.body.Rotation)) * range * item.body.Dir;
(float)Math.Sin(item.body.Rotation)) * Range * item.body.Dir;
List<Body> ignoredBodies = new List<Body>();
foreach (Limb limb in character.AnimController.Limbs)
@@ -154,9 +142,20 @@ namespace Barotrauma.Items.Components
private void Repair(Vector2 rayStart, Vector2 rayEnd, float deltaTime, Character user, float degreeOfSuccess, List<Body> ignoredBodies)
{
Body targetBody = Submarine.PickBody(rayStart, rayEnd, ignoredBodies,
Physics.CollisionWall | Physics.CollisionCharacter | Physics.CollisionItem | Physics.CollisionLevel | Physics.CollisionRepair, false);
var collisionCategories = Physics.CollisionWall | Physics.CollisionCharacter | Physics.CollisionItem | Physics.CollisionLevel | Physics.CollisionRepair;
if (RepairThroughWalls)
{
var bodies = Submarine.PickBodies(rayStart, rayEnd, ignoredBodies, collisionCategories, ignoreSensors: false);
foreach (Body body in bodies)
{
FixBody(user, deltaTime, degreeOfSuccess, body);
}
}
else
{
FixBody(user, deltaTime, degreeOfSuccess, Submarine.PickBody(rayStart, rayEnd, ignoredBodies, collisionCategories, ignoreSensors: false));
}
if (ExtinguishAmount > 0.0f && item.CurrentHull != null)
{
List<FireSource> fireSourcesInRange = new List<FireSource>();
@@ -164,7 +163,7 @@ namespace Barotrauma.Items.Components
for (float x = 0.0f; x <= Submarine.LastPickedFraction; x += 0.1f)
{
Vector2 displayPos = ConvertUnits.ToDisplayUnits(rayStart + (rayEnd - rayStart) * x);
if (item.CurrentHull.Submarine != null) { displayPos += item.CurrentHull.Submarine.Position; }
if (item.CurrentHull.Submarine != null) { displayPos += item.CurrentHull.Submarine.Position; }
Hull hull = Hull.FindHull(displayPos, item.CurrentHull);
if (hull == null) continue;
@@ -182,11 +181,14 @@ namespace Barotrauma.Items.Components
fs.Extinguish(deltaTime, ExtinguishAmount);
}
}
}
if (targetBody == null || targetBody.UserData == null) return;
private void FixBody(Character user, float deltaTime, float degreeOfSuccess, Body targetBody)
{
if (targetBody?.UserData == null) { return; }
pickedPosition = Submarine.LastPickedPosition;
if (targetBody.UserData is Structure targetStructure)
{
if (!fixableEntities.Contains("structure") && !fixableEntities.Contains(targetStructure.Prefab.Identifier)) return;
@@ -214,20 +216,14 @@ namespace Barotrauma.Items.Components
}
else if (targetBody.UserData is Character targetCharacter)
{
Vector2 hitPos = ConvertUnits.ToDisplayUnits(pickedPosition);
if (targetCharacter.Submarine != null) hitPos += targetCharacter.Submarine.Position;
targetCharacter.LastDamageSource = item;
targetCharacter.AddDamage(hitPos,
new List<Affliction>() { AfflictionPrefab.Burn.Instantiate(-LimbFixAmount * degreeOfSuccess, user) }, 0.0f, false, 0.0f, user);
ApplyStatusEffectsOnTarget(user, deltaTime, ActionType.OnUse, new List<ISerializableEntity>() { targetCharacter });
FixCharacterProjSpecific(user, deltaTime, targetCharacter);
}
else if (targetBody.UserData is Limb targetLimb)
{
targetLimb.character.LastDamageSource = item;
targetLimb.character.DamageLimb(targetLimb.WorldPosition, targetLimb,
new List<Affliction>() { AfflictionPrefab.Burn.Instantiate(-LimbFixAmount * degreeOfSuccess, user) }, 0.0f, false, 0.0f, user);
ApplyStatusEffectsOnTarget(user, deltaTime, ActionType.OnUse, new List<ISerializableEntity>() { targetLimb.character, targetLimb });
FixCharacterProjSpecific(user, deltaTime, targetLimb.character);
}
else if (targetBody.UserData is Item targetItem)
@@ -236,7 +232,7 @@ namespace Barotrauma.Items.Components
float prevCondition = targetItem.Condition;
ApplyStatusEffectsOnTarget(deltaTime, ActionType.OnUse, targetItem.AllPropertyObjects);
ApplyStatusEffectsOnTarget(user, deltaTime, ActionType.OnUse, targetItem.AllPropertyObjects);
var levelResource = targetItem.GetComponent<LevelResource>();
if (levelResource != null && levelResource.IsActive &&
@@ -254,6 +250,11 @@ namespace Barotrauma.Items.Components
FixItemProjSpecific(user, deltaTime, targetItem, prevCondition);
}
}
partial void FixStructureProjSpecific(Character user, float deltaTime, Structure targetStructure, int sectionIndex);
partial void FixCharacterProjSpecific(Character user, float deltaTime, Character targetCharacter);
partial void FixItemProjSpecific(Character user, float deltaTime, Item targetItem, float prevCondition);
partial void FixStructureProjSpecific(Character user, float deltaTime, Structure targetStructure, int sectionIndex);
partial void FixCharacterProjSpecific(Character user, float deltaTime, Character targetCharacter);
@@ -267,7 +268,7 @@ namespace Barotrauma.Items.Components
float dist = Vector2.Distance(leak.WorldPosition, item.WorldPosition);
//too far away -> consider this done and hope the AI is smart enough to move closer
if (dist > range * 5.0f) return true;
if (dist > Range * 5.0f) return true;
Vector2 gapDiff = leak.WorldPosition - item.WorldPosition;
if (!character.AnimController.InWater && character.AnimController is HumanoidAnimController &&
@@ -277,13 +278,13 @@ namespace Barotrauma.Items.Components
}
//steer closer if almost in range
if (dist > range)
if (dist > Range)
{
Vector2 standPos = leak.IsHorizontal ?
new Vector2(Math.Sign(-gapDiff.X), 0.0f)
: new Vector2(0.0f, Math.Sign(-gapDiff.Y) * 0.5f);
standPos = leak.WorldPosition + standPos * range;
standPos = leak.WorldPosition + standPos * Range;
character.AIController.SteeringManager.SteeringManual(deltaTime, (standPos - character.WorldPosition) / 1000.0f);
}
@@ -315,15 +316,14 @@ namespace Barotrauma.Items.Components
return leakFixed;
}
private void ApplyStatusEffectsOnTarget(float deltaTime, ActionType actionType, List<ISerializableEntity> targets)
private void ApplyStatusEffectsOnTarget(Character user, float deltaTime, ActionType actionType, IEnumerable<ISerializableEntity> targets)
{
if (statusEffectLists == null) return;
List<StatusEffect> statusEffects;
if (!statusEffectLists.TryGetValue(actionType, out statusEffects)) return;
if (statusEffectLists == null) { return; }
if (!statusEffectLists.TryGetValue(actionType, out List<StatusEffect> statusEffects)) { return; }
foreach (StatusEffect effect in statusEffects)
{
effect.SetUser(user);
if (effect.HasTargetType(StatusEffect.TargetType.UseTarget))
{
effect.Apply(actionType, deltaTime, item, targets);
@@ -71,7 +71,7 @@ namespace Barotrauma.Items.Components
ApplyStatusEffects(ActionType.OnActive, deltaTime, picker);
if (item.body.Dir != picker.AnimController.Dir) Flip(item);
if (item.body.Dir != picker.AnimController.Dir) Flip();
AnimController ac = picker.AnimController;
@@ -101,7 +101,9 @@ namespace Barotrauma.Items.Components
//throw upwards if cursor is at the position of the character
if (!MathUtils.IsValid(throwVector)) throwVector = Vector2.UnitY;
#if SERVER
GameServer.Log(picker.LogName + " threw " + item.Name, ServerLog.MessageType.ItemInteraction);
#endif
item.Drop();
item.body.ApplyLinearImpulse(throwVector * throwForce * item.body.Mass * 3.0f);
@@ -14,6 +14,11 @@ namespace Barotrauma.Items.Components
interface IDrawableComponent
{
#if CLIENT
/// <summary>
/// The extents of the sprites or other graphics this component needs to draw. Used to determine which items are visible on the screen.
/// </summary>
Vector2 DrawSize { get; }
void Draw(SpriteBatch spriteBatch, bool editing);
#endif
}
@@ -98,12 +103,11 @@ namespace Barotrauma.Items.Components
drawable = value;
if (drawable)
{
if (!item.drawableComponents.Contains((IDrawableComponent)this))
item.drawableComponents.Add((IDrawableComponent)this);
item.EnableDrawableComponent((IDrawableComponent)this);
}
else
{
item.drawableComponents.Remove((IDrawableComponent)this);
item.DisableDrawableComponent((IDrawableComponent)this);
}
}
}
@@ -311,7 +315,7 @@ namespace Barotrauma.Items.Components
if (ic == null) break;
ic.Parent = this;
item.components.Add(ic);
item.AddComponent(ic);
break;
}
@@ -531,6 +535,7 @@ namespace Barotrauma.Items.Components
GameAnalyticsManager.AddErrorEventOnce("ItemComponent.DegreeOfSuccess:CharacterNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
return 0.0f;
}
float average = skillSuccessSum / requiredSkills.Count;
float skillSuccessSum = 0.0f;
for (int i = 0; i < requiredSkills.Count; i++)
@@ -623,7 +628,7 @@ namespace Barotrauma.Items.Components
foreach (XAttribute attribute in componentElement.Attributes())
{
if (!properties.TryGetValue(attribute.Name.ToString().ToLowerInvariant(), out SerializableProperty property)) continue;
property.TrySetValue(attribute.Value);
property.TrySetValue(this, attribute.Value);
}
#if CLIENT
string msg = TextManager.Get(Msg, true);
@@ -141,6 +141,13 @@ namespace Barotrauma.Items.Components
effect.Apply(ActionType.OnContaining, deltaTime, item, item.AllPropertyObjects);
if (effect.HasTargetType(StatusEffect.TargetType.Contained))
effect.Apply(ActionType.OnContaining, deltaTime, item, contained.AllPropertyObjects);
if (effect.HasTargetType(StatusEffect.TargetType.NearbyItems) ||
effect.HasTargetType(StatusEffect.TargetType.NearbyCharacters))
{
var targets = new List<ISerializableEntity>();
effect.GetNearbyTargets(item.WorldPosition, targets);
effect.Apply(ActionType.OnActive, deltaTime, item, targets);
}
}
}
@@ -162,6 +169,24 @@ namespace Barotrauma.Items.Components
return base.Select(character);
}
public override bool Select(Character character)
{
if (item.Container != null) { return false; }
if (AutoInteractWithContained)
{
foreach (Item contained in Inventory.Items)
{
if (contained == null) continue;
if (contained.TryInteract(character))
{
return false;
}
}
}
return base.Select(character);
}
public override bool Pick(Character picker)
{
if (AutoInteractWithContained)
@@ -1,7 +1,15 @@
namespace Barotrauma.Items.Components
using Microsoft.Xna.Framework;
namespace Barotrauma.Items.Components
{
partial class ItemLabel : ItemComponent, IDrawableComponent
{
public Vector2 DrawSize
{
//use the extents of the item as the draw size
get { return Vector2.Zero; }
}
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0, float signalStrength = 1)
{
switch (connection.Name)
@@ -94,7 +94,7 @@ namespace Barotrauma.Items.Components
Entity.Spawner.AddToSpawnQueue(itemPrefab, outputContainer.Inventory, condition);
}
}
inputContainer.Inventory.RemoveItem(targetItem);
Entity.Spawner.AddToRemoveQueue(targetItem);
MoveInputQueue();
@@ -109,7 +109,7 @@ namespace Barotrauma.Items.Components
private void PutItemsToLinkedContainer()
{
if (GameMain.Client != null) { return; }
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
if (outputContainer.Inventory.Items.All(it => it == null)) return;
foreach (MapEntity linkedTo in item.linkedTo)
@@ -153,10 +153,14 @@ namespace Barotrauma.Items.Components
IsActive = active;
#if SERVER
if (user != null)
{
GameServer.Log(user.LogName + (IsActive ? " activated " : " deactivated ") + item.Name, ServerLog.MessageType.ItemInteraction);
}
#endif
if (!IsActive) { progressState = 0.0f; }
if (!IsActive) { progressState = 0.0f; }
@@ -174,22 +178,5 @@ namespace Barotrauma.Items.Components
inputContainer.Inventory.Locked = IsActive;
}
public void ServerRead(ClientNetObject type, NetBuffer msg, Client c)
{
bool active = msg.ReadBoolean();
item.CreateServerEvent(this);
if (item.CanClientAccess(c))
{
SetActive(active, c.Character);
}
}
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
{
msg.Write(IsActive);
}
}
}
@@ -80,7 +80,7 @@ namespace Barotrauma.Items.Components
currPowerConsumption = Math.Abs(targetForce) / 100.0f * powerConsumption;
//pumps consume more power when in a bad condition
currPowerConsumption *= MathHelper.Lerp(2.0f, 1.0f, item.Condition / 100.0f);
currPowerConsumption *= MathHelper.Lerp(2.0f, 1.0f, item.Condition / item.Prefab.Health);
if (powerConsumption == 0.0f) voltage = 1.0f;
@@ -92,7 +92,7 @@ namespace Barotrauma.Items.Components
{
Vector2 currForce = new Vector2((force / 100.0f) * maxForce * Math.Min(voltage / minVoltage, 1.0f), 0.0f);
//less effective when in a bad condition
currForce *= MathHelper.Lerp(0.5f, 2.0f, item.Condition / 100.0f);
currForce *= MathHelper.Lerp(0.5f, 2.0f, item.Condition / item.Prefab.Health);
item.Submarine.ApplyForce(currForce);
@@ -63,7 +63,7 @@ namespace Barotrauma.Items.Components
}
string displayName = element.GetAttributeString("displayname", "");
DisplayName = string.IsNullOrEmpty(displayName) ? TargetItem.Name : TextManager.Get(displayName);
DisplayName = string.IsNullOrEmpty(displayName) ? TargetItem.Name : TextManager.Get($"DisplayName.{displayName}");
RequiredSkills = new List<Skill>();
RequiredTime = element.GetAttributeFloat("requiredtime", 1.0f);
@@ -212,10 +212,12 @@ namespace Barotrauma.Items.Components
if (selectedItem == null) return;
if (!outputContainer.Inventory.IsEmpty()) return;
#if SERVER
if (user != null)
{
GameServer.Log(user.LogName + " started fabricating " + selectedItem.DisplayName + " in " + item.Name, ServerLog.MessageType.ItemInteraction);
}
#endif
#if CLIENT
itemList.Enabled = false;
@@ -235,15 +237,17 @@ namespace Barotrauma.Items.Components
outputContainer.Inventory.Locked = true;
currPowerConsumption = powerConsumption;
currPowerConsumption *= MathHelper.Lerp(2.0f, 1.0f, item.Condition / 100.0f);
currPowerConsumption *= MathHelper.Lerp(2.0f, 1.0f, item.Condition / item.Prefab.Health);
}
private void CancelFabricating(Character user = null)
{
#if SERVER
if (fabricatedItem != null && user != null)
{
GameServer.Log(user.LogName + " cancelled the fabrication of " + fabricatedItem.DisplayName + " in " + item.Name, ServerLog.MessageType.ItemInteraction);
}
#endif
IsActive = false;
fabricatedItem = null;
@@ -315,7 +319,12 @@ namespace Barotrauma.Items.Components
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, outputContainer.Inventory, fabricatedItem.TargetItem.Health * fabricatedItem.OutCondition);
}
if (GameMain.Client == null && user != null)
bool isNotClient = true;
#if CLIENT
isNotClient = GameMain.Client == null;
#endif
if (isNotClient && user != null)
{
foreach (Skill skill in fabricatedItem.RequiredSkills)
{
@@ -426,38 +435,5 @@ namespace Barotrauma.Items.Components
item.prefab == requiredItem.ItemPrefab &&
item.Condition / item.Prefab.Health >= requiredItem.MinCondition;
}
public void ServerRead(ClientNetObject type, NetBuffer msg, Client c)
{
int itemIndex = msg.ReadRangedInteger(-1, fabricableItems.Count - 1);
item.CreateServerEvent(this);
if (!item.CanClientAccess(c)) return;
if (itemIndex == -1)
{
CancelFabricating(c.Character);
}
else
{
//if already fabricating the selected item, return
if (fabricatedItem != null && fabricableItems.IndexOf(fabricatedItem) == itemIndex) return;
if (itemIndex < 0 || itemIndex >= fabricableItems.Count) return;
#if CLIENT
SelectItem(c.Character, fabricableItems[itemIndex]);
#endif
StartFabricating(fabricableItems[itemIndex], c.Character);
}
}
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
{
int itemIndex = fabricatedItem == null ? -1 : fabricableItems.IndexOf(fabricatedItem);
msg.WriteRangedInteger(-1, fabricableItems.Count - 1, itemIndex);
UInt16 userID = fabricatedItem == null || user == null ? (UInt16)0 : user.ID;
msg.Write(userID);
}
}
}
@@ -73,7 +73,7 @@ namespace Barotrauma.Items.Components
}
currPowerConsumption = powerConsumption;
currPowerConsumption *= MathHelper.Lerp(2.0f, 1.0f, item.Condition / 100.0f);
currPowerConsumption *= MathHelper.Lerp(2.0f, 1.0f, item.Condition / item.Prefab.Health);
hasPower = voltage > minVoltage;
if (hasPower)
@@ -42,7 +42,7 @@ namespace Barotrauma.Items.Components
CurrFlow = 0.0f;
currPowerConsumption = powerConsumption;
//consume more power when in a bad condition
currPowerConsumption *= MathHelper.Lerp(2.0f, 1.0f, item.Condition / 100.0f);
currPowerConsumption *= MathHelper.Lerp(2.0f, 1.0f, item.Condition / item.Prefab.Health);
if (powerConsumption <= 0.0f)
{
@@ -63,7 +63,7 @@ namespace Barotrauma.Items.Components
CurrFlow = Math.Min(voltage, 1.0f) * generatedAmount * 100.0f;
//less effective when in bad condition
CurrFlow *= MathHelper.Lerp(0.5f, 1.0f, item.Condition / 100.0f);
CurrFlow *= MathHelper.Lerp(0.5f, 1.0f, item.Condition / item.Prefab.Health);
UpdateVents(CurrFlow);
@@ -66,7 +66,7 @@ namespace Barotrauma.Items.Components
currPowerConsumption = powerConsumption * Math.Abs(flowPercentage / 100.0f);
//pumps consume more power when in a bad condition
currPowerConsumption *= MathHelper.Lerp(2.0f, 1.0f, item.Condition / 100.0f);
currPowerConsumption *= MathHelper.Lerp(2.0f, 1.0f, item.Condition / item.Prefab.Health);
if (voltage < minVoltage) return;
@@ -82,7 +82,7 @@ namespace Barotrauma.Items.Components
currFlow = flowPercentage / 100.0f * maxFlow * powerFactor;
//less effective when in a bad condition
currFlow *= MathHelper.Lerp(0.5f, 1.0f, item.Condition / 100.0f);
currFlow *= MathHelper.Lerp(0.5f, 1.0f, item.Condition / item.Prefab.Health);
item.CurrentHull.WaterVolume += currFlow;
if (item.CurrentHull.WaterVolume > item.CurrentHull.Volume) { item.CurrentHull.Pressure += 0.5f; }
@@ -124,55 +124,29 @@ namespace Barotrauma.Items.Components
public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
{
#if CLIENT
if (GameMain.Client != null) return false;
#endif
if (objective.Option.ToLowerInvariant() == "stoppumping")
{
#if SERVER
if (FlowPercentage > 0.0f) item.CreateServerEvent(this);
#endif
FlowPercentage = 0.0f;
}
else
{
#if SERVER
if (!IsActive || FlowPercentage > -100.0f)
{
item.CreateServerEvent(this);
}
#endif
IsActive = true;
FlowPercentage = -100.0f;
}
return true;
}
public void ServerRead(ClientNetObject type, Lidgren.Network.NetBuffer msg, Client c)
{
float newFlowPercentage = msg.ReadRangedInteger(-10, 10) * 10.0f;
bool newIsActive = msg.ReadBoolean();
if (item.CanClientAccess(c))
{
if (newFlowPercentage != FlowPercentage)
{
GameServer.Log(c.Character.LogName + " set the pumping speed of " + item.Name + " to " + (int)(newFlowPercentage) + " %", ServerLog.MessageType.ItemInteraction);
}
if (newIsActive != IsActive)
{
GameServer.Log(c.Character.LogName + (newIsActive ? " turned on " : " turned off ") + item.Name, ServerLog.MessageType.ItemInteraction);
}
FlowPercentage = newFlowPercentage;
IsActive = newIsActive;
}
//notify all clients of the changed state
item.CreateServerEvent(this);
}
public void ServerWrite(Lidgren.Network.NetBuffer msg, Client c, object[] extraData = null)
{
//flowpercentage can only be adjusted at 10% intervals -> no need for more accuracy than this
msg.WriteRangedInteger(-10, 10, (int)(flowPercentage / 10.0f));
msg.Write(IsActive);
}
}
}
@@ -24,9 +24,7 @@ namespace Barotrauma.Items.Components
//(adjusts the fission rate and turbine output automatically to keep the
//amount of power generated balanced with the load)
private bool autoTemp;
private Client BlameOnBroken;
//automatical adjustment to the power output when
//turbine output and temperature are in the optimal range
private float autoAdjustAmount;
@@ -44,10 +42,7 @@ namespace Barotrauma.Items.Components
private float sendUpdateTimer;
private float degreeOfSuccess;
private float? nextServerLogWriteTime;
private float lastServerLogWriteTime;
private Vector2 optimalTemperature, allowedTemperature;
private Vector2 optimalFissionRate, allowedFissionRate;
private Vector2 optimalTurbineOutput, allowedTurbineOutput;
@@ -174,6 +169,7 @@ namespace Barotrauma.Items.Components
public override void Update(float deltaTime, Camera cam)
{
#if SERVER
if (GameMain.Server != null && nextServerLogWriteTime != null)
{
if (Timing.TotalTime >= (float)nextServerLogWriteTime)
@@ -189,6 +185,7 @@ namespace Barotrauma.Items.Components
lastServerLogWriteTime = (float)Timing.TotalTime;
}
}
#endif
prevAvailableFuel = AvailableFuel;
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
@@ -260,8 +257,7 @@ namespace Barotrauma.Items.Components
if (!connection.IsPower) continue;
foreach (Connection recipient in connection.Recipients)
{
Item it = recipient.Item as Item;
if (it == null) continue;
if (!(recipient.Item is Item it)) continue;
PowerTransfer pt = it.GetComponent<PowerTransfer>();
if (pt == null) continue;
@@ -300,12 +296,14 @@ namespace Barotrauma.Items.Components
if (unsentChanges && sendUpdateTimer <= 0.0f)
{
#if SERVER
if (GameMain.Server != null)
{
item.CreateServerEvent(this);
}
#endif
#if CLIENT
else if (GameMain.Client != null)
if (GameMain.Client != null)
{
item.CreateClientEvent(this);
}
@@ -321,7 +319,7 @@ namespace Barotrauma.Items.Components
{
item.SendSignal(0, "1", "meltdown_warning", null);
//faster meltdown if the item is in a bad condition
meltDownTimer += MathHelper.Lerp(deltaTime * 2.0f, deltaTime, item.Condition / 100.0f);
meltDownTimer += MathHelper.Lerp(deltaTime * 2.0f, deltaTime, item.Condition / item.Prefab.Health);
if (meltDownTimer > MeltdownDelay)
{
@@ -338,7 +336,7 @@ namespace Barotrauma.Items.Components
if (temperature > optimalTemperature.Y)
{
float prevFireTimer = fireTimer;
fireTimer += MathHelper.Lerp(deltaTime * 2.0f, deltaTime, item.Condition / 100.0f);
fireTimer += MathHelper.Lerp(deltaTime * 2.0f, deltaTime, item.Condition / item.Prefab.Health);
if (fireTimer >= FireDelay && prevFireTimer < fireDelay)
{
@@ -388,9 +386,14 @@ namespace Barotrauma.Items.Components
private void MeltDown()
{
if (item.Condition <= 0.0f || GameMain.Client != null) return;
if (item.Condition <= 0.0f) return;
#if CLIENT
if (GameMain.Client != null) return;
#endif
#if SERVER
GameServer.Log("Reactor meltdown!", ServerLog.MessageType.ItemInteraction);
#endif
item.Condition = 0.0f;
fireTimer = 0.0f;
@@ -405,11 +408,13 @@ namespace Barotrauma.Items.Components
containedItem.Condition = 0.0f;
}
}
if (GameMain.Server != null && GameMain.Server.ConnectedClients.Contains(BlameOnBroken))
#if SERVER
if (GameMain.Server != null && GameMain.Server.ConnectedClients.Contains(blameOnBroken))
{
BlameOnBroken.Karma = 0.0f;
}
blameOnBroken.Karma = 0.0f;
}
#endif
}
public override bool Pick(Character picker)
@@ -419,7 +424,7 @@ namespace Barotrauma.Items.Components
public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
{
if (GameMain.Client != null) return false;
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return false; }
float degreeOfSuccess = DegreeOfSuccess(character);
@@ -532,49 +537,5 @@ namespace Barotrauma.Items.Components
}
}
public void ServerRead(ClientNetObject type, NetBuffer msg, Client c)
{
bool autoTemp = msg.ReadBoolean();
bool shutDown = msg.ReadBoolean();
float fissionRate = msg.ReadRangedSingle(0.0f, 100.0f, 8);
float turbineOutput = msg.ReadRangedSingle(0.0f, 100.0f, 8);
if (!item.CanClientAccess(c)) return;
if (!autoTemp && AutoTemp) BlameOnBroken = c;
if (turbineOutput < targetTurbineOutput) BlameOnBroken = c;
if (fissionRate > targetFissionRate) BlameOnBroken = c;
if (!this.shutDown && shutDown) BlameOnBroken = c;
AutoTemp = autoTemp;
this.shutDown = shutDown;
targetFissionRate = fissionRate;
targetTurbineOutput = turbineOutput;
LastUser = c.Character;
if (nextServerLogWriteTime == null)
{
nextServerLogWriteTime = Math.Max(lastServerLogWriteTime + 1.0f, (float)Timing.TotalTime);
}
#if CLIENT
fissionRateScrollBar.BarScroll = 1.0f - targetFissionRate / 100.0f;
turbineOutputScrollBar.BarScroll = 1.0f - targetTurbineOutput / 100.0f;
onOffSwitch.BarScroll = shutDown ? Math.Max(onOffSwitch.BarScroll, 0.55f) : Math.Min(onOffSwitch.BarScroll, 0.45f);
#endif
//need to create a server event to notify all clients of the changed state
unsentChanges = true;
}
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
{
msg.Write(autoTemp);
msg.Write(shutDown);
msg.WriteRangedSingle(temperature, 0.0f, 100.0f, 8);
msg.WriteRangedSingle(targetFissionRate, 0.0f, 100.0f, 8);
msg.WriteRangedSingle(targetTurbineOutput, 0.0f, 100.0f, 8);
msg.WriteRangedSingle(degreeOfSuccess, 0.0f, 1.0f, 8);
}
}
}
@@ -313,6 +313,8 @@ namespace Barotrauma.Items.Components
if (!item.CanClientAccess(c)) return;
IsActive = isActive;
//TODO: cleanup
#if CLIENT
activeTickBox.Selected = IsActive;
#endif
@@ -331,8 +333,9 @@ namespace Barotrauma.Items.Components
directionalSlider.BarScroll = pingDirectionT;
#endif
}
#if SERVER
item.CreateServerEvent(this);
#endif
}
public void ServerWrite(Lidgren.Network.NetBuffer msg, Client c, object[] extraData = null)
@@ -187,10 +187,12 @@ namespace Barotrauma.Items.Components
}
else
#endif
#if SERVER
if (GameMain.Server != null)
{
item.CreateServerEvent(this);
}
#endif
networkUpdateTimer = 0.1f;
unsentChanges = false;
@@ -22,9 +22,6 @@ namespace Barotrauma.Items.Components
//how fast it's currently being recharged (can be changed, so that
//charging can be slowed down or disabled if there's a shortage of power)
private float rechargeSpeed;
private float maxOutput;
private float lastSentCharge;
//charge indicator description
@@ -32,6 +29,14 @@ namespace Barotrauma.Items.Components
protected bool isHorizontal;
//a list of powered devices connected directly to this item
private readonly List<Pair<Powered, Connection>> directlyConnected = new List<Pair<Powered, Connection>>(10);
//charge indicator description
protected Vector2 indicatorPosition, indicatorSize;
protected bool isHorizontal;
public float CurrPowerOutput
{
get;
@@ -60,11 +65,7 @@ namespace Barotrauma.Items.Components
}
[Editable(ToolTip = "Maximum output of the device when fully charged (kW)."), Serialize(10.0f, true)]
public float MaxOutPut
{
set { maxOutput = value; }
get { return maxOutput; }
}
public float MaxOutPut { set; get; }
[Serialize(10.0f, true), Editable(ToolTip = "The maximum capacity of the device (kW * min). "+
"For example, a value of 1000 means the device can output 100 kilowatts of power for 10 minutes, or 1000 kilowatts for 1 minute.")]
@@ -86,7 +87,9 @@ namespace Barotrauma.Items.Components
//send a network event if the charge has changed by more than 5%
if (Math.Abs(charge - lastSentCharge) / capacity > 0.05f)
{
#if SERVER
if (GameMain.Server != null) item.CreateServerEvent(this);
#endif
lastSentCharge = charge;
}
}
@@ -129,11 +132,11 @@ namespace Barotrauma.Items.Components
public override void Update(float deltaTime, Camera cam)
{
float chargeRatio = (float)(Math.Sqrt(charge / capacity));
float chargeRatio = charge / capacity;
float gridPower = 0.0f;
float gridLoad = 0.0f;
directlyConnected.Clear();
List<Pair<Powered, Connection>> directlyConnected = new List<Pair<Powered, Connection>>();
foreach (Connection c in item.Connections)
{
if (c.Name == "power_in") continue;
@@ -187,9 +190,16 @@ namespace Barotrauma.Items.Components
if (gridPower < gridLoad)
{
//output starts dropping when the charge is less than 10%
float maxOutputRatio = 1.0f;
if (chargeRatio < 0.1f)
{
maxOutputRatio = Math.Max(chargeRatio * 10.0f, 0.0f);
}
CurrPowerOutput = MathHelper.Lerp(
CurrPowerOutput,
Math.Min(maxOutput * chargeRatio, gridLoad),
Math.Min(MaxOutPut * maxOutputRatio, gridLoad),
deltaTime * 10.0f);
}
else
@@ -212,13 +222,17 @@ namespace Barotrauma.Items.Components
public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
{
#if CLIENT
if (GameMain.Client != null) return false;
#endif
if (string.IsNullOrEmpty(objective.Option) || objective.Option.ToLowerInvariant() == "charge")
{
if (Math.Abs(rechargeSpeed - maxRechargeSpeed * 0.5f) > 0.05f)
{
#if SERVER
item.CreateServerEvent(this);
#endif
RechargeSpeed = maxRechargeSpeed * 0.5f;
#if CLIENT
rechargeSpeedSlider.BarScroll = RechargeSpeed / Math.Max(maxRechargeSpeed, 1.0f);
@@ -232,7 +246,9 @@ namespace Barotrauma.Items.Components
{
if (rechargeSpeed > 0.0f)
{
#if SERVER
item.CreateServerEvent(this);
#endif
RechargeSpeed = 0.0f;
#if CLIENT
rechargeSpeedSlider.BarScroll = RechargeSpeed / Math.Max(maxRechargeSpeed, 1.0f);
@@ -259,26 +275,5 @@ namespace Barotrauma.Items.Components
outputVoltage = power;
}
}
public void ServerRead(ClientNetObject type, NetBuffer msg, Client c)
{
float newRechargeSpeed = msg.ReadRangedInteger(0, 10) / 10.0f * maxRechargeSpeed;
if (item.CanClientAccess(c))
{
RechargeSpeed = newRechargeSpeed;
GameServer.Log(c.Character.LogName + " set the recharge speed of " + item.Name + " to " + (int)((rechargeSpeed / maxRechargeSpeed) * 100.0f) + " %", ServerLog.MessageType.ItemInteraction);
}
item.CreateServerEvent(this);
}
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
{
msg.WriteRangedInteger(0, 10, (int)(rechargeSpeed / MaxRechargeSpeed * 10));
float chargeRatio = MathHelper.Clamp(charge / capacity, 0.0f, 1.0f);
msg.WriteRangedSingle(chargeRatio, 0.0f, 1.0f, 8);
}
}
}
@@ -169,9 +169,14 @@ namespace Barotrauma.Items.Components
pt.Item.SendSignal(0, "", "power", null, voltage);
pt.Item.SendSignal(0, "", "power_out", null, voltage);
#if CLIENT
//damage the item if voltage is too high
//(except if running as a client)
if (GameMain.Client != null) continue;
#endif
//items in a bad condition are more sensitive to overvoltage
float maxOverVoltage = MathHelper.Lerp(Math.Min(OverloadVoltage, 1.0f), OverloadVoltage, item.Condition / item.Prefab.Health);
//items in a bad condition are more sensitive to overvoltage
float maxOverVoltage = MathHelper.Lerp(Math.Min(OverloadVoltage, 1.0f), OverloadVoltage, item.Condition / 100.0f);
@@ -313,26 +318,24 @@ namespace Barotrauma.Items.Components
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
float maxPower = this is RelayComponent relayComponent ? relayComponent.MaxPower : float.PositiveInfinity;
foreach (Connection c in PowerConnections)
{
var recipients = c.Recipients;
foreach (Connection recipient in recipients)
{
if (recipient == null) continue;
if (recipient?.Item == null) continue;
Item it = recipient.Item;
if (it == null) continue;
if (it.Condition <= 0.0f) continue;
foreach (ItemComponent ic in it.components)
foreach (ItemComponent ic in it.Components)
{
Powered powered = ic as Powered;
if (powered == null || !powered.IsActive) continue;
if (!(ic is Powered powered) || !powered.IsActive) continue;
if (connectedList.Contains(powered)) continue;
PowerTransfer powerTransfer = powered as PowerTransfer;
if (powerTransfer != null)
if (powered is PowerTransfer powerTransfer)
{
if (this is RelayComponent == powerTransfer is RelayComponent)
{
@@ -348,8 +351,7 @@ namespace Barotrauma.Items.Components
continue;
}
PowerContainer powerContainer = powered as PowerContainer;
if (powerContainer != null)
if (powered is PowerContainer powerContainer)
{
if (recipient.Name == "power_in")
{
@@ -357,7 +359,7 @@ namespace Barotrauma.Items.Components
}
else
{
fullPower += powerContainer.CurrPowerOutput;
fullPower += Math.Min(powerContainer.CurrPowerOutput, maxPower);
}
}
else
@@ -372,7 +374,7 @@ namespace Barotrauma.Items.Components
//negative power consumption = the construction is a
//generator/battery or another junction box
{
fullPower -= powered.CurrPowerConsumption;
fullPower -= Math.Max(powered.CurrPowerConsumption, -maxPower);
}
}
}
@@ -422,7 +424,7 @@ namespace Barotrauma.Items.Components
{
if (recipient.Item == item || recipient.Item == source) continue;
foreach (ItemComponent ic in recipient.Item.components)
foreach (ItemComponent ic in recipient.Item.Components)
{
//powertransfer components don't need to receive the signal in the pass-through signal connections
//because we relay it straight to the connected items without going through the whole chain of junction boxes
@@ -12,9 +12,7 @@ namespace Barotrauma.Items.Components
public static float SkillIncreaseMultiplier = 0.4f;
private string header;
private float lastSentProgress;
private float fixDurationLowSkill, fixDurationHighSkill;
private float deteriorationTimer;
@@ -71,7 +69,7 @@ namespace Barotrauma.Items.Components
get { return currentFixer; }
set
{
if (currentFixer == value || item.Condition >= 100.0f) return;
if (currentFixer == value || item.Condition >= item.Prefab.Health) return;
if (currentFixer != null) currentFixer.AnimController.Anim = AnimController.Animation.None;
currentFixer = value;
}
@@ -93,8 +91,11 @@ namespace Barotrauma.Items.Components
public override void OnItemLoaded()
{
deteriorationTimer = Rand.Range(MinDeteriorationDelay, MaxDeteriorationDelay);
#if SERVER
//let the clients know the initial deterioration delay
item.CreateServerEvent(this);
#endif
}
partial void InitProjSpecific(XElement element);
@@ -119,10 +120,12 @@ namespace Barotrauma.Items.Components
{
if (deteriorationTimer > 0.0f)
{
if (GameMain.Client == null)
if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
{
deteriorationTimer -= deltaTime;
#if SERVER
if (deteriorationTimer <= 0.0f) { item.CreateServerEvent(this); }
#endif
}
return;
}
@@ -144,7 +147,7 @@ namespace Barotrauma.Items.Components
UpdateFixAnimation(CurrentFixer);
if (GameMain.Client != null) return;
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
float successFactor = requiredSkills.Count == 0 ? 1.0f : 0.0f;
foreach (Skill skill in requiredSkills)
@@ -166,10 +169,14 @@ namespace Barotrauma.Items.Components
{
item.Condition += deltaTime / (fixDuration / item.Prefab.Health);
}
if (wasBroken && item.Condition >= item.Prefab.Health)
{
SteamAchievementManager.OnItemRepaired(item, currentFixer);
deteriorationTimer = Rand.Range(MinDeteriorationDelay, MaxDeteriorationDelay);
#if SERVER
item.CreateServerEvent(this);
#endif
}
}
@@ -180,7 +180,7 @@ namespace Barotrauma.Items.Components
source.LastSentSignalRecipients.Add(recipient.item);
}
foreach (ItemComponent ic in recipient.item.components)
foreach (ItemComponent ic in recipient.item.Components)
{
ic.ReceiveSignal(stepsTaken, signal, recipient, source, sender, power, signalStrength);
}
@@ -220,144 +220,5 @@ namespace Barotrauma.Items.Components
}
}
}
public void ServerRead(ClientNetObject type, NetBuffer msg, Client c)
{
List<Wire>[] wires = new List<Wire>[Connections.Count];
//read wire IDs for each connection
for (int i = 0; i < Connections.Count; i++)
{
wires[i] = new List<Wire>();
for (int j = 0; j < Connection.MaxLinked; j++)
{
ushort wireId = msg.ReadUInt16();
Item wireItem = Entity.FindEntityByID(wireId) as Item;
if (wireItem == null) continue;
Wire wireComponent = wireItem.GetComponent<Wire>();
if (wireComponent != null)
{
wires[i].Add(wireComponent);
}
}
}
//don't allow rewiring locked panels
if (Locked) return;
item.CreateServerEvent(this);
//check if the character can access this connectionpanel
//and all the wires they're trying to connect
if (!item.CanClientAccess(c)) return;
for (int i = 0; i < Connections.Count; i++)
{
foreach (Wire wire in wires[i])
{
//wire not found in any of the connections yet (client is trying to connect a new wire)
// -> we need to check if the client has access to it
if (!Connections.Any(connection => connection.Wires.Contains(wire)))
{
if (!wire.Item.CanClientAccess(c)) return;
}
}
}
//go through existing wire links
for (int i = 0; i < Connections.Count; i++)
{
int j = -1;
foreach (Wire existingWire in Connections[i].Wires)
{
j++;
if (existingWire == null) continue;
//existing wire not in the list of new wires -> disconnect it
if (!wires[i].Contains(existingWire))
{
if (existingWire.Locked)
{
//this should not be possible unless the client is running a modified version of the game
GameServer.Log(c.Character.LogName + " attempted to disconnect a locked wire from " +
Connections[i].Item.Name + " (" + Connections[i].Name + ")", ServerLog.MessageType.Error);
continue;
}
existingWire.RemoveConnection(item);
if (existingWire.Connections[0] == null && existingWire.Connections[1] == null)
{
GameServer.Log(c.Character.LogName + " disconnected a wire from " +
Connections[i].Item.Name + " (" + Connections[i].Name + ")", ServerLog.MessageType.ItemInteraction);
}
else if (existingWire.Connections[0] != null)
{
GameServer.Log(c.Character.LogName + " disconnected a wire from " +
Connections[i].Item.Name + " (" + Connections[i].Name + ") to " + existingWire.Connections[0].Item.Name + " (" + existingWire.Connections[0].Name + ")", ServerLog.MessageType.ItemInteraction);
//wires that are not in anyone's inventory (i.e. not currently being rewired)
//can never be connected to only one connection
// -> the client must have dropped the wire from the connection panel
if (existingWire.Item.ParentInventory == null && !wires.Any(w => w.Contains(existingWire)))
{
//let other clients know the item was also disconnected from the other connection
existingWire.Connections[0].Item.CreateServerEvent(existingWire.Connections[0].Item.GetComponent<ConnectionPanel>());
existingWire.Item.Drop(c.Character);
}
}
else if (existingWire.Connections[1] != null)
{
GameServer.Log(c.Character.LogName + " disconnected a wire from " +
Connections[i].Item.Name + " (" + Connections[i].Name + ") to " + existingWire.Connections[1].Item.Name + " (" + existingWire.Connections[1].Name + ")", ServerLog.MessageType.ItemInteraction);
if (existingWire.Item.ParentInventory == null && !wires.Any(w => w.Contains(existingWire)))
{
//let other clients know the item was also disconnected from the other connection
existingWire.Connections[1].Item.CreateServerEvent(existingWire.Connections[1].Item.GetComponent<ConnectionPanel>());
existingWire.Item.Drop(c.Character);
}
}
Connections[i].SetWire(j, null);
}
}
}
//go through new wires
for (int i = 0; i < Connections.Count; i++)
{
foreach (Wire newWire in wires[i])
{
//already connected, no need to do anything
if (Connections[i].Wires.Contains(newWire)) continue;
Connections[i].TryAddLink(newWire);
newWire.Connect(Connections[i], true, true);
var otherConnection = newWire.OtherConnection(Connections[i]);
if (otherConnection == null)
{
GameServer.Log(c.Character.LogName + " connected a wire to " +
Connections[i].Item.Name + " (" + Connections[i].Name + ")",
ServerLog.MessageType.ItemInteraction);
}
else
{
GameServer.Log(c.Character.LogName + " connected a wire from " +
Connections[i].Item.Name + " (" + Connections[i].Name + ") to " +
(otherConnection == null ? "none" : otherConnection.Item.Name + " (" + (otherConnection.Name) + ")"),
ServerLog.MessageType.ItemInteraction);
}
}
}
}
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
{
ClientWrite(msg, extraData);
}
}
}
@@ -137,57 +137,5 @@ namespace Barotrauma.Items.Components
item.SendSignal(0, ciElement.State ? ciElement.Signal : "0", ciElement.Connection, sender: null, source: item);
}
}
public void ServerRead(ClientNetObject type, NetBuffer msg, Client c)
{
bool[] elementStates = new bool[customInterfaceElementList.Count];
for (int i = 0; i < customInterfaceElementList.Count; i++)
{
elementStates[i] = msg.ReadBoolean();
}
CustomInterfaceElement clickedButton = null;
if (item.CanClientAccess(c))
{
for (int i = 0; i < customInterfaceElementList.Count; i++)
{
if (customInterfaceElementList[i].ContinuousSignal)
{
TickBoxToggled(customInterfaceElementList[i], elementStates[i]);
}
else if (elementStates[i])
{
clickedButton = customInterfaceElementList[i];
ButtonClicked(customInterfaceElementList[i]);
}
}
}
//notify all clients of the new state
GameMain.Server.CreateEntityEvent(item, new object[]
{
NetEntityEvent.Type.ComponentState,
item.components.IndexOf(this),
clickedButton
});
item.CreateServerEvent(this);
}
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
{
//extradata contains an array of buttons clicked by a client (or nothing if nothing was clicked)
for (int i = 0; i < customInterfaceElementList.Count; i++)
{
if (customInterfaceElementList[i].ContinuousSignal)
{
msg.Write(customInterfaceElementList[i].State);
}
else
{
msg.Write(extraData != null && extraData.Any(d => d as CustomInterfaceElement == customInterfaceElementList[i]));
}
}
}
}
}
@@ -76,7 +76,9 @@ namespace Barotrauma.Items.Components
if (IsActive == value) return;
IsActive = value;
#if SERVER
if (GameMain.Server != null) item.CreateServerEvent(this);
#endif
}
}
@@ -152,6 +154,7 @@ namespace Barotrauma.Items.Components
#endif
IsActive = IsOn;
item.AddTag("light");
}
public override void Update(float deltaTime, Camera cam)
@@ -174,7 +177,7 @@ namespace Barotrauma.Items.Components
if (body != null)
{
#if CLIENT
light.Rotation = body.Dir > 0.0f ? body.Rotation : body.Rotation - MathHelper.Pi;
light.Rotation = body.Dir > 0.0f ? body.DrawRotation : body.DrawRotation - MathHelper.Pi;
light.LightSpriteEffect = (body.Dir > 0.0f) ? SpriteEffects.None : SpriteEffects.FlipVertically;
#endif
if (!body.Enabled)
@@ -234,9 +237,16 @@ namespace Barotrauma.Items.Components
light.Color = lightColor * lightBrightness * (1.0f - Rand.Range(0.0f, Flicker));
light.Range = range;
#endif
item.SightRange = Math.Max(range * (float)Math.Sqrt(lightBrightness), item.SightRange);
}
if (AITarget != null)
{
UpdateAITarget(AITarget);
}
if (item.AiTarget != null)
{
UpdateAITarget(item.AiTarget);
}
voltage = 0.0f;
}
@@ -279,5 +289,16 @@ namespace Barotrauma.Items.Components
{
msg.Write(IsOn);
}
private void UpdateAITarget(AITarget target)
{
//voltage > minVoltage || powerConsumption <= 0.0f; <- ?
target.Enabled = IsActive;
if (target.MaxSightRange <= 0)
{
target.MaxSightRange = Range * 5;
}
target.SightRange = IsActive ? target.MaxSightRange * lightBrightness : 0;
}
}
}
@@ -79,6 +79,13 @@ namespace Barotrauma.Items.Components
set { falseOutput = value; }
}
[Editable(ToolTip = "How fast the objects within the detector's range have to be moving (in m/s).", DecimalCount = 3), Serialize(0.01f, true)]
public float MinimumVelocity
{
get;
set;
}
public MotionSensor(Item item, XElement element)
: base (item, element)
@@ -106,7 +113,7 @@ namespace Barotrauma.Items.Components
if (item.body != null && item.body.Enabled)
{
if (Math.Abs(item.body.LinearVelocity.X) > 0.01f || Math.Abs(item.body.LinearVelocity.Y) > 0.1f)
if (Math.Abs(item.body.LinearVelocity.X) > MinimumVelocity || Math.Abs(item.body.LinearVelocity.Y) > MinimumVelocity)
{
motionDetected = true;
}
@@ -130,7 +137,7 @@ namespace Barotrauma.Items.Components
foreach (Limb limb in c.AnimController.Limbs)
{
if (limb.LinearVelocity.LengthSquared() <= 0.001f) continue;
if (limb.LinearVelocity.LengthSquared() <= MinimumVelocity * MinimumVelocity) continue;
if (MathUtils.CircleIntersectsRectangle(limb.WorldPosition, ConvertUnits.ToDisplayUnits(limb.body.GetMaxExtent()), detectRect))
{
motionDetected = true;
@@ -51,7 +51,10 @@ namespace Barotrauma.Items.Components
item.SendSignal(0, IsOn ? "1" : "0", "state_out", null);
if (Math.Min(-currPowerConsumption, PowerLoad) > maxPower) item.Condition = 0.0f;
if (Math.Min(-currPowerConsumption, PowerLoad) > maxPower && CanBeOverloaded)
{
item.Condition = 0.0f;
}
}
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0.0f, float signalStrength = 1.0f)
@@ -81,12 +84,16 @@ namespace Barotrauma.Items.Components
public void SetState(bool on, bool isNetworkMessage)
{
#if CLIENT
if (GameMain.Client != null && !isNetworkMessage) return;
#endif
#if SERVER
if (on != IsOn && GameMain.Server != null)
{
item.CreateServerEvent(this);
}
#endif
IsOn = on;
}
@@ -6,6 +6,12 @@ namespace Barotrauma.Items.Components
{
private string output, falseOutput;
//how often the detector can switch from state to another
const float StateSwitchInterval = 1.0f;
private bool isInWater;
private float stateSwitchDelay;
[InGameEditable, Serialize("1", true)]
public string Output
{
@@ -28,22 +34,37 @@ namespace Barotrauma.Items.Components
public override void Update(float deltaTime, Camera cam)
{
string signalOut = falseOutput;
if (item.InWater)
if (stateSwitchDelay > 0.0f)
{
//item in water -> we definitely want to send the True output
signalOut = Output;
stateSwitchDelay -= deltaTime;
}
else if (item.CurrentHull != null)
else
{
//item in not water -> check if there's water anywhere within the rect of the item
if (item.CurrentHull.Surface > item.CurrentHull.Rect.Y - item.CurrentHull.Rect.Height + 1 &&
item.CurrentHull.Surface > item.Rect.Y - item.Rect.Height)
bool prevState = isInWater;
isInWater = false;
if (item.InWater)
{
signalOut = output;
//item in water -> we definitely want to send the True output
isInWater = true;
}
else if (item.CurrentHull != null)
{
//item in not water -> check if there's water anywhere within the rect of the item
if (item.CurrentHull.Surface > item.CurrentHull.Rect.Y - item.CurrentHull.Rect.Height + 1 &&
item.CurrentHull.Surface > item.Rect.Y - item.Rect.Height)
{
isInWater = true;
}
}
if (prevState != isInWater)
{
stateSwitchDelay = StateSwitchInterval;
}
}
string signalOut = isInWater ? output : falseOutput;
if (!string.IsNullOrEmpty(signalOut))
{
item.SendSignal(0, signalOut, "signal_out", null);
@@ -18,7 +18,7 @@ namespace Barotrauma.Items.Components
private string prevSignal;
public byte TeamID;
public Character.TeamType TeamID;
[Serialize(20000.0f, false)]
public float Range
@@ -134,12 +134,16 @@ namespace Barotrauma.Items.Components
if (chatMsg.Length > ChatMessage.MaxLength) chatMsg = chatMsg.Substring(0, ChatMessage.MaxLength);
if (string.IsNullOrEmpty(chatMsg)) continue;
#if CLIENT
if (wifiComp.item.ParentInventory.Owner == Character.Controlled)
{
if (GameMain.Client == null)
GameMain.NetworkMember.AddChatMessage(signal, ChatMessageType.Radio, source == null ? "" : source.Name);
}
else if (GameMain.Server != null)
#endif
#if SERVER
if (GameMain.Server != null)
{
Client recipientClient = GameMain.Server.ConnectedClients.Find(c => c.Character == wifiComp.item.ParentInventory.Owner);
if (recipientClient != null)
@@ -148,6 +152,7 @@ namespace Barotrauma.Items.Components
ChatMessage.Create(source == null ? "" : source.Name, chatMsg, ChatMessageType.Radio, null), recipientClient);
}
}
#endif
chatMsgSent = true;
}
}
@@ -14,13 +14,24 @@ namespace Barotrauma.Items.Components
partial class WireSection
{
private Vector2 start;
private Vector2 end;
private float angle;
private float length;
public Vector2 Start
{
get { return start; }
}
public Vector2 End
{
get { return end; }
}
public WireSection(Vector2 start, Vector2 end)
{
this.start = start;
this.end = end;
angle = MathUtils.VectorToAngle(end - start);
length = Vector2.Distance(start, end);
@@ -41,6 +52,8 @@ namespace Barotrauma.Items.Components
private bool canPlaceNode;
private Vector2 newNodePos;
private Vector2 sectionExtents;
public bool Hidden;
private bool locked;
@@ -185,7 +198,7 @@ namespace Barotrauma.Items.Components
if (connections[0] != null && connections[1] != null)
{
foreach (ItemComponent ic in item.components)
foreach (ItemComponent ic in item.Components)
{
if (ic == this) continue;
ic.Drop(null);
@@ -202,10 +215,12 @@ namespace Barotrauma.Items.Components
if (sendNetworkEvent)
{
#if SERVER
if (GameMain.Server != null)
{
CreateNetworkEvent();
}
#endif
//the wire is active if only one end has been connected
IsActive = connections[0] == null ^ connections[1] == null;
}
@@ -274,8 +289,7 @@ namespace Barotrauma.Items.Components
//prevent the wire from extending too far when rewiring
if (nodes.Count > 0)
{
Character user = item.ParentInventory?.Owner as Character;
if (user == null) return;
if (!(item.ParentInventory?.Owner is Character user)) return;
Vector2 prevNodePos = nodes[nodes.Count - 1];
if (sub != null) { prevNodePos += sub.HiddenSubPosition; }
@@ -294,11 +308,17 @@ namespace Barotrauma.Items.Components
user.AnimController.Collider.ApplyForce(pullBackDir * user.Mass * 50.0f);
user.AnimController.UpdateUseItem(true, user.WorldPosition + pullBackDir * 200.0f);
if (currLength > MaxLength * 1.5f && GameMain.Client == null)
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
{
ClearConnections();
CreateNetworkEvent();
return;
if (currLength > MaxLength * 1.5f)
{
ClearConnections();
#if SERVER
CreateNetworkEvent();
#endif
return;
}
}
}
}
@@ -308,12 +328,18 @@ namespace Barotrauma.Items.Components
newNodePos = RoundNode(item.Position, item.CurrentHull) - sub.HiddenSubPosition;
canPlaceNode = true;
}
sectionExtents = new Vector2(
Math.Max(Math.Abs((newNodePos.X + sub.HiddenSubPosition.X) - item.Position.X), sectionExtents.X),
Math.Max(Math.Abs((newNodePos.Y + sub.HiddenSubPosition.Y) - item.Position.Y), sectionExtents.Y));
}
public override bool Use(float deltaTime, Character character = null)
{
if (character == null) return false;
#if CLIENT
if (character == Character.Controlled && character.SelectedConstruction != null) return false;
#endif
if (newNodePos != Vector2.Zero && canPlaceNode && nodes.Count > 0 && Vector2.Distance(newNodePos, nodes[nodes.Count - 1]) > nodeDistance)
{
@@ -328,10 +354,12 @@ namespace Barotrauma.Items.Components
Drawable = true;
newNodePos = Vector2.Zero;
#if SERVER
if (GameMain.Server != null)
{
CreateNetworkEvent();
}
#endif
}
return true;
}
@@ -393,18 +421,33 @@ namespace Barotrauma.Items.Components
{
sections.Clear();
for (int i = 0; i < nodes.Count-1; i++)
for (int i = 0; i < nodes.Count - 1; i++)
{
sections.Add(new WireSection(nodes[i], nodes[i + 1]));
}
Drawable = IsActive || sections.Count > 0;
CalculateExtents();
}
private void CalculateExtents()
{
sectionExtents = Vector2.Zero;
if (sections.Count > 0)
{
for (int i = 0; i < nodes.Count; i++)
{
sectionExtents.X = Math.Max(Math.Abs(nodes[i].X - item.Position.X), sectionExtents.X);
sectionExtents.Y = Math.Max(Math.Abs(nodes[i].Y - item.Position.Y), sectionExtents.Y);
}
}
}
private void ClearConnections(Character user = null)
{
nodes.Clear();
sections.Clear();
#if SERVER
if (user != null)
{
if (connections[0] != null && connections[1] != null)
@@ -424,6 +467,7 @@ namespace Barotrauma.Items.Components
connections[1].Item.Name + " (" + connections[1].Name + ")", ServerLog.MessageType.ItemInteraction);
}
}
#endif
SetConnectedDirty();
@@ -627,33 +671,6 @@ namespace Barotrauma.Items.Components
base.RemoveComponentSpecific();
}
private void CreateNetworkEvent()
{
if (GameMain.Server == null) return;
//split into multiple events because one might not be enough to fit all the nodes
int eventCount = Math.Max((int)Math.Ceiling(nodes.Count / (float)MaxNodesPerNetworkEvent), 1);
for (int i = 0; i < eventCount; i++)
{
GameMain.Server.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ComponentState, item.components.IndexOf(this), i });
}
}
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
{
int eventIndex = (int)extraData[2];
int nodeStartIndex = eventIndex * MaxNodesPerNetworkEvent;
int nodeCount = MathHelper.Clamp(nodes.Count - nodeStartIndex, 0, MaxNodesPerNetworkEvent);
msg.WriteRangedInteger(0, (int)Math.Ceiling(MaxNodeCount / (float)MaxNodesPerNetworkEvent), eventIndex);
msg.WriteRangedInteger(0, MaxNodesPerNetworkEvent, nodeCount);
for (int i = nodeStartIndex; i < nodeStartIndex + nodeCount; i++)
{
msg.Write(nodes[i].X);
msg.Write(nodes[i].Y);
}
}
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
{
int eventIndex = msg.ReadRangedInteger(0, (int)Math.Ceiling(MaxNodeCount / (float)MaxNodesPerNetworkEvent));
@@ -189,6 +189,7 @@ namespace Barotrauma.Items.Components
#if CLIENT
if (lightComponent != null)
{
lightComponent.Parent = null;
lightComponent.Rotation = rotation;
lightComponent.Light.Rotation = -rotation;
}
@@ -258,7 +259,9 @@ namespace Barotrauma.Items.Components
private bool TryLaunch(float deltaTime, Character character = null)
{
#if CLIENT
if (GameMain.Client != null) return false;
#endif
if (reload > 0.0f) return false;
@@ -313,27 +316,32 @@ namespace Barotrauma.Items.Components
battery.Charge -= takePower / 3600.0f;
#if SERVER
if (GameMain.Server != null)
{
battery.Item.CreateServerEvent(battery);
}
#endif
}
Launch(projectiles[0].Item, character);
#if SERVER
if (character != null)
{
string msg = character.LogName + " launched " + item.Name + " (projectile: " + projectiles[0].Item.Name;
if (projectiles[0].Item.ContainedItems == null || projectiles[0].Item.ContainedItems.All(i => i == null))
var containedItems = projectiles[0].Item.ContainedItems;
if (containedItems == null || !containedItems.Any())
{
msg += ")";
}
else
{
msg += ", contained items: " + string.Join(", ", Array.FindAll(projectiles[0].Item.ContainedItems, i => i != null).Select(i => i.Name)) + ")";
msg += ", contained items: " + string.Join(", ", containedItems.Select(i => i.Name)) + ")";
}
GameServer.Log(msg, ServerLog.MessageType.ItemInteraction);
}
#endif
return true;
}
@@ -359,10 +367,10 @@ namespace Barotrauma.Items.Components
}
if (projectile.Container != null) projectile.Container.RemoveContained(projectile);
if (GameMain.Server != null)
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
{
GameMain.Server.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ComponentState, item.components.IndexOf(this), projectile });
GameMain.NetworkMember.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ComponentState, item.GetComponentIndex(this), projectile });
}
ApplyStatusEffects(ActionType.OnUse, 1.0f, user: user);
@@ -415,18 +423,9 @@ namespace Barotrauma.Items.Components
if (containedItems != null)
{
var container = projectileContainer.GetComponent<ItemContainer>();
if (containedItems != null) maxProjectileCount += container.Capacity;
int projectiles = 0;
for (int i = 0; i < containedItems.Length; i++)
{
if (containedItems[i].Condition > 0.0f)
{
projectiles++;
}
}
maxProjectileCount += container.Capacity;
int projectiles = containedItems.Count(it => it.Condition > 0.0f);
usableProjectileCount += projectiles;
}
}
@@ -465,14 +464,21 @@ namespace Barotrauma.Items.Components
foreach (Character enemy in Character.CharacterList)
{
//ignore humans and characters that are inside the sub
if (enemy.IsDead || enemy.SpeciesName == "human" || enemy.AnimController.CurrentHull != null) continue;
if (enemy.IsDead|| enemy.AnimController.CurrentHull != null || !enemy.Enabled) { continue; }
if (enemy.SpeciesName == character.SpeciesName && enemy.TeamID == character.TeamID) { continue; }
float dist = Vector2.DistanceSquared(enemy.WorldPosition, item.WorldPosition);
if (dist < closestDist)
{
closestEnemy = enemy;
closestDist = dist;
}
if (dist > closestDist) { continue; }
float angle = -MathUtils.VectorToAngle(enemy.WorldPosition - item.WorldPosition);
float midRotation = (minRotation + maxRotation) / 2.0f;
while (midRotation - angle < -MathHelper.Pi) { angle -= MathHelper.TwoPi; }
while (midRotation - angle > MathHelper.Pi) { angle += MathHelper.TwoPi; }
if (angle < minRotation || angle > maxRotation) { continue; }
closestEnemy = enemy;
closestDist = dist;
}
if (closestEnemy == null) return false;
@@ -486,7 +492,7 @@ namespace Barotrauma.Items.Components
float enemyAngle = MathUtils.VectorToAngle(closestEnemy.WorldPosition - item.WorldPosition);
float turretAngle = -rotation;
if (Math.Abs(MathUtils.GetShortestAngle(enemyAngle, turretAngle)) > 0.1f) return false;
if (Math.Abs(MathUtils.GetShortestAngle(enemyAngle, turretAngle)) > 0.15f) return false;
var pickedBody = Submarine.PickBody(ConvertUnits.ToSimUnits(item.WorldPosition), closestEnemy.SimPosition, null);
if (pickedBody != null && !(pickedBody.UserData is Limb)) return false;
@@ -559,9 +565,9 @@ namespace Barotrauma.Items.Components
var containedItems = projectileContainer.ContainedItems;
if (containedItems == null) return;
for (int i = 0; i < containedItems.Length; i++)
foreach (Item containedItem in containedItems)
{
var projectileComponent = containedItems[i].GetComponent<Projectile>();
var projectileComponent = containedItem.GetComponent<Projectile>();
if (projectileComponent != null)
{
projectiles.Add(projectileComponent);
@@ -570,10 +576,10 @@ namespace Barotrauma.Items.Components
else
{
//check if the contained item is another itemcontainer with projectiles inside it
if (containedItems[i].ContainedItems == null) continue;
for (int j = 0; j < containedItems[i].ContainedItems.Length; j++)
if (containedItem.ContainedItems == null) continue;
foreach (Item subContainedItem in containedItem.ContainedItems)
{
projectileComponent = containedItems[i].ContainedItems[j].GetComponent<Projectile>();
projectileComponent = subContainedItem.GetComponent<Projectile>();
if (projectileComponent != null)
{
projectiles.Add(projectileComponent);
@@ -653,12 +659,9 @@ namespace Barotrauma.Items.Components
break;
case "toggle":
case "toggle_light":
foreach (ItemComponent component in item.components)
if (lightComponent != null)
{
if (component.Parent == this && component is LightComponent lightComponent)
{
lightComponent.IsOn = !lightComponent.IsOn;
}
lightComponent.IsOn = !lightComponent.IsOn;
}
break;
}
@@ -197,9 +197,11 @@ namespace Barotrauma.Items.Components
foreach (XElement lightElement in subElement.Elements())
{
if (lightElement.Name.ToString().ToLowerInvariant() != "lightcomponent") continue;
wearableSprites[i].LightComponent = new LightComponent(item, lightElement);
wearableSprites[i].LightComponent.Parent = this;
item.components.Add(wearableSprites[i].LightComponent);
wearableSprites[i].LightComponent = new LightComponent(item, lightElement)
{
Parent = this
};
item.AddComponent(wearableSprites[i].LightComponent);
}
i++;
@@ -225,9 +227,12 @@ namespace Barotrauma.Items.Components
}
Limb equipLimb = character.AnimController.GetLimb(limbType[i]);
if (equipLimb == null) continue;
if (equipLimb == null) { continue; }
item.body.Enabled = false;
if (item.body != null)
{
item.body.Enabled = false;
}
IsActive = true;
if (wearableSprite.LightComponent != null)
{