Merge branch 'master' of https://github.com/Regalis11/Barotrauma.git
This commit is contained in:
@@ -99,8 +99,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (!docked && value)
|
||||
{
|
||||
if (DockingTarget == null) AttemptDock();
|
||||
if (DockingTarget == null) return;
|
||||
if (DockingTarget == null) { AttemptDock(); }
|
||||
if (DockingTarget == null) { return; }
|
||||
|
||||
docked = true;
|
||||
}
|
||||
@@ -126,6 +126,14 @@ namespace Barotrauma.Items.Components
|
||||
/// </summary>
|
||||
public event Action OnUnDocked;
|
||||
|
||||
private bool outpostAutoDockingPromptShown;
|
||||
|
||||
enum AllowOutpostAutoDocking
|
||||
{
|
||||
Ask, Yes, No
|
||||
}
|
||||
private AllowOutpostAutoDocking allowOutpostAutoDocking = AllowOutpostAutoDocking.Ask;
|
||||
|
||||
public DockingPort(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
@@ -613,7 +621,7 @@ namespace Barotrauma.Items.Components
|
||||
hullRects[i].X -= expand;
|
||||
hullRects[i].Width += expand * 2;
|
||||
hullRects[i].Location -= MathUtils.ToPoint(subs[i].WorldPosition - subs[i].HiddenSubPosition);
|
||||
hulls[i] = new Hull(MapEntityPrefab.Find(null, "hull"), hullRects[i], subs[i]);
|
||||
hulls[i] = new Hull(hullRects[i], subs[i]);
|
||||
hulls[i].RoomName = IsHorizontal ? "entityname.dockingport" : "entityname.dockinghatch";
|
||||
hulls[i].AddToGrid(subs[i]);
|
||||
hulls[i].FreeID();
|
||||
@@ -622,7 +630,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
bodies[i + j * 2] = GameMain.World.CreateEdge(
|
||||
ConvertUnits.ToSimUnits(new Vector2(hullRects[i].X, hullRects[i].Y - hullRects[i].Height * j)),
|
||||
ConvertUnits.ToSimUnits(new Vector2(hullRects[i].Right, hullRects[i].Y - hullRects[i].Height * j)));
|
||||
ConvertUnits.ToSimUnits(new Vector2(hullRects[i].Right, hullRects[i].Y - hullRects[i].Height * j)),
|
||||
BodyType.Static);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -632,7 +641,9 @@ namespace Barotrauma.Items.Components
|
||||
ConvertUnits.ToSimUnits(hullRects[0].Width + hullRects[1].Width),
|
||||
ConvertUnits.ToSimUnits(hullRects[0].Height),
|
||||
density: 0.0f,
|
||||
offset: ConvertUnits.ToSimUnits(new Vector2(hullRects[0].Right, hullRects[0].Y - hullRects[0].Height / 2) - hulls[0].Submarine.HiddenSubPosition));
|
||||
offset: ConvertUnits.ToSimUnits(new Vector2(hullRects[0].Right, hullRects[0].Y - hullRects[0].Height / 2) - hulls[0].Submarine.HiddenSubPosition),
|
||||
Physics.CollisionWall,
|
||||
Physics.CollisionCharacter | Physics.CollisionItem | Physics.CollisionCharacter | Physics.CollisionItemBlocking | Physics.CollisionProjectile);
|
||||
outsideBlocker.UserData = this;
|
||||
}
|
||||
|
||||
@@ -733,7 +744,7 @@ namespace Barotrauma.Items.Components
|
||||
hullRects[i].Y += expand;
|
||||
hullRects[i].Height += expand * 2;
|
||||
hullRects[i].Location -= MathUtils.ToPoint(subs[i].WorldPosition - subs[i].HiddenSubPosition);
|
||||
hulls[i] = new Hull(MapEntityPrefab.Find(null, "hull"), hullRects[i], subs[i]);
|
||||
hulls[i] = new Hull(hullRects[i], subs[i]);
|
||||
hulls[i].RoomName = IsHorizontal ? "entityname.dockingport" : "entityname.dockinghatch";
|
||||
hulls[i].AddToGrid(subs[i]);
|
||||
hulls[i].FreeID();
|
||||
@@ -742,7 +753,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
bodies[i + j * 2] = GameMain.World.CreateEdge(
|
||||
ConvertUnits.ToSimUnits(new Vector2(hullRects[i].X + hullRects[i].Width * j, hullRects[i].Y)),
|
||||
ConvertUnits.ToSimUnits(new Vector2(hullRects[i].X + hullRects[i].Width * j, hullRects[i].Y - hullRects[i].Height)));
|
||||
ConvertUnits.ToSimUnits(new Vector2(hullRects[i].X + hullRects[i].Width * j, hullRects[i].Y - hullRects[i].Height)),
|
||||
BodyType.Static);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -752,7 +764,9 @@ namespace Barotrauma.Items.Components
|
||||
ConvertUnits.ToSimUnits(hullRects[0].Width),
|
||||
ConvertUnits.ToSimUnits(hullRects[0].Height + hullRects[1].Height),
|
||||
density: 0.0f,
|
||||
offset: ConvertUnits.ToSimUnits(new Vector2(hullRects[0].Center.X, hullRects[0].Y) - hulls[0].Submarine.HiddenSubPosition));
|
||||
offset: ConvertUnits.ToSimUnits(new Vector2(hullRects[0].Center.X, hullRects[0].Y) - hulls[0].Submarine.HiddenSubPosition),
|
||||
Physics.CollisionWall,
|
||||
Physics.CollisionCharacter | Physics.CollisionItem | Physics.CollisionCharacter | Physics.CollisionItemBlocking | Physics.CollisionProjectile);
|
||||
outsideBlocker.UserData = this;
|
||||
}
|
||||
|
||||
@@ -778,8 +792,6 @@ namespace Barotrauma.Items.Components
|
||||
if (body == null) { continue; }
|
||||
body.BodyType = BodyType.Static;
|
||||
body.Friction = 0.5f;
|
||||
|
||||
body.CollisionCategories = Physics.CollisionWall;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -947,7 +959,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
foreach (Body body in bodies)
|
||||
{
|
||||
if (body == null) continue;
|
||||
if (body == null) { continue; }
|
||||
GameMain.World.Remove(body);
|
||||
}
|
||||
bodies = null;
|
||||
@@ -961,6 +973,9 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
item.CreateServerEvent(this);
|
||||
}
|
||||
#elif CLIENT
|
||||
autodockingVerification?.Close();
|
||||
autodockingVerification = null;
|
||||
#endif
|
||||
OnUnDocked?.Invoke();
|
||||
OnUnDocked = null;
|
||||
@@ -1140,27 +1155,86 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void ReceiveSignal(Signal signal, Connection connection)
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
|
||||
#if CLIENT
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient &&
|
||||
!(GameMain.GameSession?.Campaign?.AllowedToManageCampaign(ClientPermissions.ManageMap) ?? false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (dockingCooldown > 0.0f) { return; }
|
||||
|
||||
bool wasDocked = docked;
|
||||
DockingPort prevDockingTarget = DockingTarget;
|
||||
|
||||
bool newDockedState = wasDocked;
|
||||
switch (connection.Name)
|
||||
{
|
||||
case "toggle":
|
||||
if (signal.value != "0")
|
||||
{
|
||||
Docked = !docked;
|
||||
newDockedState = !docked;
|
||||
}
|
||||
break;
|
||||
case "set_active":
|
||||
case "set_state":
|
||||
Docked = signal.value != "0";
|
||||
newDockedState = signal.value != "0";
|
||||
break;
|
||||
}
|
||||
|
||||
if (newDockedState != wasDocked)
|
||||
{
|
||||
bool tryingToToggleOutpostDocking = docked ?
|
||||
DockingTarget?.Item?.Submarine?.Info?.IsOutpost ?? false :
|
||||
FindAdjacentPort()?.Item?.Submarine?.Info?.IsOutpost ?? false;
|
||||
//trying to dock/undock from an outpost and the signal was sent by some automated system instead of a character
|
||||
// -> ask if the player really wants to dock/undock to prevent a softlock if someone's wired the docking port
|
||||
// in a way that makes always makes it dock/undock immediately at the start of the roun
|
||||
if (tryingToToggleOutpostDocking && signal.sender == null)
|
||||
{
|
||||
if (allowOutpostAutoDocking == AllowOutpostAutoDocking.Ask)
|
||||
{
|
||||
#if CLIENT
|
||||
if (!outpostAutoDockingPromptShown)
|
||||
{
|
||||
autodockingVerification = new GUIMessageBox(string.Empty,
|
||||
TextManager.Get(newDockedState ? "autodockverification" : "autoundockverification"),
|
||||
new LocalizedString[] { TextManager.Get("Yes"), TextManager.Get("No") });
|
||||
autodockingVerification.Buttons[0].OnClicked += (btn, userdata) =>
|
||||
{
|
||||
autodockingVerification?.Close();
|
||||
autodockingVerification = null;
|
||||
if (item.Removed || GameMain.Client == null) { return false; }
|
||||
allowOutpostAutoDocking = AllowOutpostAutoDocking.Yes;
|
||||
item.CreateClientEvent(this);
|
||||
return true;
|
||||
};
|
||||
autodockingVerification.Buttons[1].OnClicked += (btn, userdata) =>
|
||||
{
|
||||
autodockingVerification?.Close();
|
||||
autodockingVerification = null;
|
||||
if (item.Removed || GameMain.Client == null) { return false; }
|
||||
allowOutpostAutoDocking = AllowOutpostAutoDocking.No;
|
||||
item.CreateClientEvent(this);
|
||||
return true;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
outpostAutoDockingPromptShown = true;
|
||||
return;
|
||||
}
|
||||
else if (allowOutpostAutoDocking == AllowOutpostAutoDocking.No)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
|
||||
|
||||
Docked = newDockedState;
|
||||
}
|
||||
|
||||
#if SERVER
|
||||
if (signal.sender != null && docked != wasDocked)
|
||||
{
|
||||
|
||||
@@ -241,12 +241,14 @@ namespace Barotrauma.Items.Components
|
||||
Body = new PhysicsBody(
|
||||
ConvertUnits.ToSimUnits(Math.Max(doorRect.Width, 1)),
|
||||
ConvertUnits.ToSimUnits(Math.Max(doorRect.Height, 1)),
|
||||
0.0f,
|
||||
1.5f)
|
||||
radius: 0.0f,
|
||||
density: 1.5f,
|
||||
BodyType.Static,
|
||||
Physics.CollisionWall,
|
||||
Physics.CollisionCharacter | Physics.CollisionItem | Physics.CollisionCharacter | Physics.CollisionItemBlocking | Physics.CollisionProjectile,
|
||||
findNewContacts: false)
|
||||
{
|
||||
UserData = item,
|
||||
CollisionCategories = Physics.CollisionWall,
|
||||
BodyType = BodyType.Static,
|
||||
Friction = 0.5f
|
||||
};
|
||||
Body.SetTransformIgnoreContacts(
|
||||
@@ -258,12 +260,16 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override void Move(Vector2 amount)
|
||||
public override void Move(Vector2 amount, bool ignoreContacts = false)
|
||||
{
|
||||
base.Move(amount);
|
||||
|
||||
Body?.SetTransform(Body.SimPosition + ConvertUnits.ToSimUnits(amount), 0.0f);
|
||||
|
||||
if (ignoreContacts)
|
||||
{
|
||||
Body?.SetTransformIgnoreContacts(Body.SimPosition + ConvertUnits.ToSimUnits(amount), 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
Body?.SetTransform(Body.SimPosition + ConvertUnits.ToSimUnits(amount), 0.0f);
|
||||
}
|
||||
#if CLIENT
|
||||
UpdateConvexHulls();
|
||||
#endif
|
||||
|
||||
@@ -144,9 +144,16 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public bool Combine(GeneticMaterial otherGeneticMaterial, Character user)
|
||||
public enum CombineResult
|
||||
{
|
||||
if (!CanBeCombinedWith(otherGeneticMaterial)) { return false; }
|
||||
None,
|
||||
Refined,
|
||||
Combined
|
||||
}
|
||||
|
||||
public CombineResult Combine(GeneticMaterial otherGeneticMaterial, Character user)
|
||||
{
|
||||
if (!CanBeCombinedWith(otherGeneticMaterial)) { return CombineResult.None; }
|
||||
|
||||
float conditionIncrease = Rand.Range(ConditionIncreaseOnCombineMin, ConditionIncreaseOnCombineMax);
|
||||
conditionIncrease += user?.GetStatValue(StatTypes.GeneticMaterialRefineBonus) ?? 0.0f;
|
||||
@@ -158,7 +165,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
MakeTainted();
|
||||
}
|
||||
return true;
|
||||
return CombineResult.Refined;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -171,7 +178,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
MakeTainted();
|
||||
}
|
||||
return false;
|
||||
return CombineResult.Combined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private Character prevEquipper;
|
||||
|
||||
public override bool IsAttached => Attached;
|
||||
|
||||
private bool attachable, attached, attachedByDefault;
|
||||
private Voronoi2.VoronoiCell attachTargetCell;
|
||||
private PhysicsBody body;
|
||||
@@ -71,6 +73,7 @@ namespace Barotrauma.Items.Components
|
||||
set
|
||||
{
|
||||
attached = value;
|
||||
item.CheckCleanable();
|
||||
item.SetActiveSprite();
|
||||
}
|
||||
}
|
||||
@@ -164,6 +167,8 @@ namespace Barotrauma.Items.Components
|
||||
public bool SwingWhenAiming { get; set; }
|
||||
[Editable, Serialize(false, IsPropertySaveable.No, description: "Should the item swing around when it's being used (for example, when firing a weapon or a welding tool).")]
|
||||
public bool SwingWhenUsing { get; set; }
|
||||
[Editable, Serialize(false, IsPropertySaveable.No)]
|
||||
public bool DisableHeadRotation { get; set; }
|
||||
|
||||
[ConditionallyEditable(ConditionallyEditable.ConditionType.Attachable, MinValueFloat = 0.0f, MaxValueFloat = 0.999f, DecimalCount = 3), Serialize(0.55f, IsPropertySaveable.No, description: "Sprite depth that's used when the item is NOT attached to a wall.")]
|
||||
public float SpriteDepthWhenDropped
|
||||
@@ -180,11 +185,12 @@ namespace Barotrauma.Items.Components
|
||||
Pusher = null;
|
||||
if (element.GetAttributeBool("blocksplayers", false))
|
||||
{
|
||||
Pusher = new PhysicsBody(item.body.width, item.body.height, item.body.radius, item.body.Density)
|
||||
Pusher = new PhysicsBody(item.body.width, item.body.height, item.body.radius,
|
||||
item.body.Density,
|
||||
BodyType.Dynamic,
|
||||
Physics.CollisionItemBlocking,
|
||||
Physics.CollisionCharacter | Physics.CollisionProjectile)
|
||||
{
|
||||
BodyType = BodyType.Dynamic,
|
||||
CollidesWith = Physics.CollisionCharacter | Physics.CollisionProjectile,
|
||||
CollisionCategories = Physics.CollisionItemBlocking,
|
||||
Enabled = false,
|
||||
UserData = this
|
||||
};
|
||||
|
||||
+16
-8
@@ -3,7 +3,6 @@ using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -79,11 +78,18 @@ namespace Barotrauma.Items.Components
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public override void Move(Vector2 amount)
|
||||
public override void Move(Vector2 amount, bool ignoreContacts = false)
|
||||
{
|
||||
if (trigger != null && amount.LengthSquared() > 0.00001f)
|
||||
{
|
||||
trigger.SetTransform(item.SimPosition, 0.0f);
|
||||
if (ignoreContacts)
|
||||
{
|
||||
trigger.SetTransformIgnoreContacts(item.SimPosition, 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
trigger.SetTransform(item.SimPosition, 0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,17 +125,19 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
var body = item.body ?? holdable.Body;
|
||||
|
||||
|
||||
if (body != null)
|
||||
{
|
||||
trigger = new PhysicsBody(body.width, body.height, body.radius, body.Density)
|
||||
trigger = new PhysicsBody(body.width, body.height, body.radius,
|
||||
body.Density,
|
||||
BodyType.Static,
|
||||
Physics.CollisionWall,
|
||||
Physics.CollisionNone,
|
||||
findNewContacts: false)
|
||||
{
|
||||
UserData = item
|
||||
};
|
||||
trigger.FarseerBody.SetIsSensor(true);
|
||||
trigger.FarseerBody.BodyType = BodyType.Static;
|
||||
trigger.FarseerBody.CollisionCategories = Physics.CollisionWall;
|
||||
trigger.FarseerBody.CollidesWith = Physics.CollisionNone;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -111,8 +111,9 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
ActivateNearbySleepingCharacters();
|
||||
reloadTimer = reload;
|
||||
reloadTimer /= (1f + character.GetStatValue(StatTypes.MeleeAttackSpeed));
|
||||
reloadTimer /= (1f + item.GetQualityModifier(Quality.StatType.StrikingSpeedMultiplier));
|
||||
reloadTimer /= 1f + character.GetStatValue(StatTypes.MeleeAttackSpeed);
|
||||
reloadTimer /= 1f + item.GetQualityModifier(Quality.StatType.StrikingSpeedMultiplier);
|
||||
character.AnimController.LockFlippingUntil = (float)Timing.TotalTime + reloadTimer;
|
||||
|
||||
item.body.FarseerBody.CollisionCategories = Physics.CollisionProjectile;
|
||||
item.body.FarseerBody.CollidesWith = Physics.CollisionCharacter | Physics.CollisionWall | Physics.CollisionItemBlocking;
|
||||
@@ -216,6 +217,10 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
hitPos = MathUtils.WrapAnglePi(Math.Min(hitPos + deltaTime * 3f, MathHelper.PiOver4));
|
||||
ac.HoldItem(deltaTime, item, handlePos, aimPos, Vector2.Zero, aim: false, hitPos, holdAngle + hitPos, aimMelee: true);
|
||||
if (ac.InWater)
|
||||
{
|
||||
ac.LockFlippingUntil = (float)Timing.TotalTime + Reload;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -4,7 +4,6 @@ using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -20,6 +19,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private CoroutineHandle pickingCoroutine;
|
||||
|
||||
public virtual bool IsAttached => false;
|
||||
|
||||
public List<InvSlotType> AllowedSlots
|
||||
{
|
||||
get { return allowedSlots; }
|
||||
@@ -71,6 +72,7 @@ namespace Barotrauma.Items.Components
|
||||
//return if someone is already trying to pick the item
|
||||
if (pickTimer > 0.0f) { return false; }
|
||||
if (picker == null || picker.Inventory == null) { return false; }
|
||||
if (!picker.Inventory.AccessibleWhenAlive && !picker.Inventory.AccessibleByOwner) { return false; }
|
||||
|
||||
if (PickingTime > 0.0f)
|
||||
{
|
||||
@@ -226,7 +228,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
foreach (Connection c in connectionPanel.Connections)
|
||||
{
|
||||
foreach (Wire w in c.Wires)
|
||||
foreach (Wire w in c.Wires.ToArray())
|
||||
{
|
||||
if (w == null) continue;
|
||||
w.Item.Drop(character);
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override bool Use(float deltaTime, Character character = null)
|
||||
{
|
||||
if (character == null || character.Removed) return false;
|
||||
if (character == null || character.Removed) { return false; }
|
||||
if (!character.IsKeyDown(InputType.Aim) || character.Stun > 0.0f) { return false; }
|
||||
|
||||
IsActive = true;
|
||||
@@ -55,12 +55,11 @@ namespace Barotrauma.Items.Components
|
||||
if (UsableIn == UseEnvironment.Water) { return true; }
|
||||
}
|
||||
|
||||
Vector2 dir = Vector2.Normalize(character.CursorPosition - character.Position);
|
||||
//move upwards if the cursor is at the position of the character
|
||||
if (!MathUtils.IsValid(dir)) dir = Vector2.UnitY;
|
||||
|
||||
Vector2 dir = character.CursorPosition - character.Position;
|
||||
if (!MathUtils.IsValid(dir)) { return true; }
|
||||
float length = 200;
|
||||
dir = dir.ClampLength(length) / length;
|
||||
Vector2 propulsion = dir * Force * character.PropulsionSpeedMultiplier;
|
||||
|
||||
if (character.AnimController.InWater && Force > 0.0f) { character.AnimController.TargetMovement = dir; }
|
||||
|
||||
foreach (Limb limb in character.AnimController.Limbs)
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
get
|
||||
{
|
||||
Matrix bodyTransform = Matrix.CreateRotationZ(item.body == null ? MathHelper.ToRadians(item.Rotation) : item.body.Rotation);
|
||||
Matrix bodyTransform = Matrix.CreateRotationZ(item.body == null ? item.RotationRad : item.body.Rotation);
|
||||
Vector2 flippedPos = barrelPos;
|
||||
if (item.body != null && item.body.Dir < 0.0f) { flippedPos.X = -flippedPos.X; }
|
||||
return Vector2.Transform(flippedPos, bodyTransform) * item.Scale;
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace Barotrauma.Items.Components
|
||||
get; set;
|
||||
}
|
||||
|
||||
[Serialize(true, IsPropertySaveable.No, description: "Can the item hit broken doors.")]
|
||||
[Serialize(true, IsPropertySaveable.No, description: "Can the item hit doors.")]
|
||||
public bool HitItems { get; set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No, description: "Can the item hit broken doors.")]
|
||||
@@ -697,14 +697,17 @@ namespace Barotrauma.Items.Components
|
||||
Vector2 fromCharacterToLeak = leak.WorldPosition - character.AnimController.AimSourceWorldPos;
|
||||
float dist = fromCharacterToLeak.Length();
|
||||
float reach = AIObjectiveFixLeak.CalculateReach(this, character);
|
||||
|
||||
if (dist > reach * 3)
|
||||
if (dist > reach * 2)
|
||||
{
|
||||
// Too far away -> consider this done and hope the AI is smart enough to move closer
|
||||
Reset();
|
||||
return true;
|
||||
}
|
||||
character.AIController.SteeringManager.Reset();
|
||||
if (character.AIController.SteeringManager is IndoorsSteeringManager pathSteering)
|
||||
{
|
||||
pathSteering.ResetPath();
|
||||
}
|
||||
if (!character.AnimController.InWater)
|
||||
{
|
||||
// TODO: use the collider size?
|
||||
@@ -714,34 +717,25 @@ namespace Barotrauma.Items.Components
|
||||
humanAnim.Crouching = true;
|
||||
}
|
||||
}
|
||||
if (dist > reach * 0.8f || dist > reach * 0.5f && character.AnimController.Limbs.Any(l => l.InWater))
|
||||
if (!character.IsClimbing)
|
||||
{
|
||||
// Steer closer
|
||||
if (character.AIController.SteeringManager is IndoorsSteeringManager indoorSteering)
|
||||
if (dist > reach * 0.8f || dist > reach * 0.5f && character.AnimController.Limbs.Any(l => l.InWater))
|
||||
{
|
||||
// Swimming inside the sub
|
||||
if (indoorSteering.CurrentPath != null && !indoorSteering.IsPathDirty && (indoorSteering.CurrentPath.Unreachable || indoorSteering.CurrentPath.Finished))
|
||||
// Steer closer
|
||||
Vector2 dir = Vector2.Normalize(fromCharacterToLeak);
|
||||
if (!character.InWater)
|
||||
{
|
||||
Vector2 dir = Vector2.Normalize(fromCharacterToLeak);
|
||||
character.AIController.SteeringManager.SteeringManual(deltaTime, dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
character.AIController.SteeringManager.SteeringSeek(character.GetRelativeSimPosition(leak));
|
||||
dir.Y = 0;
|
||||
}
|
||||
character.AIController.SteeringManager.SteeringManual(deltaTime, dir);
|
||||
}
|
||||
else
|
||||
else if (dist < reach * 0.25f && !character.IsClimbing)
|
||||
{
|
||||
// Swimming outside the sub
|
||||
character.AIController.SteeringManager.SteeringSeek(character.GetRelativeSimPosition(leak));
|
||||
// Too close -> steer away
|
||||
character.AIController.SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(character.SimPosition - leak.SimPosition));
|
||||
}
|
||||
}
|
||||
else if (dist < reach * 0.25f)
|
||||
{
|
||||
// Too close -> steer away
|
||||
character.AIController.SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(character.SimPosition - leak.SimPosition));
|
||||
}
|
||||
if (dist <= reach)
|
||||
if (dist <= reach || character.IsClimbing)
|
||||
{
|
||||
// In range
|
||||
character.CursorPosition = leak.WorldPosition;
|
||||
@@ -815,7 +809,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
bool leakFixed = (leak.Open <= 0.0f || leak.Removed) &&
|
||||
(leak.ConnectedWall == null || leak.ConnectedWall.Sections.Average(s => s.damage) < 1);
|
||||
(leak.ConnectedWall == null || leak.ConnectedWall.Sections.Max(s => s.damage) < 0.1f);
|
||||
|
||||
if (leakFixed && leak.FlowTargetHull?.DisplayName != null && character.IsOnPlayerTeam)
|
||||
{
|
||||
|
||||
@@ -390,7 +390,7 @@ namespace Barotrauma.Items.Components
|
||||
IsActive = isActive;
|
||||
}
|
||||
|
||||
public void SetRequiredItems(ContentXElement element)
|
||||
public void SetRequiredItems(ContentXElement element, bool allowEmpty = false)
|
||||
{
|
||||
bool returnEmpty = false;
|
||||
#if CLIENT
|
||||
@@ -412,13 +412,13 @@ namespace Barotrauma.Items.Components
|
||||
requiredItems[ri.Type].Add(ri);
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (!allowEmpty)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in item config \"" + item.ConfigFilePath + "\" - component " + GetType().ToString() + " requires an item with no identifiers.");
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Move(Vector2 amount) { }
|
||||
public virtual void Move(Vector2 amount, bool ignoreContacts = false) { }
|
||||
|
||||
/// <summary>a Character has picked the item</summary>
|
||||
public virtual bool Pick(Character picker)
|
||||
|
||||
@@ -17,18 +17,20 @@ namespace Barotrauma.Items.Components
|
||||
public readonly Item Item;
|
||||
public readonly StatusEffect StatusEffect;
|
||||
public readonly bool ExcludeBroken;
|
||||
public ActiveContainedItem(Item item, StatusEffect statusEffect, bool excludeBroken)
|
||||
public readonly bool ExcludeFullCondition;
|
||||
public ActiveContainedItem(Item item, StatusEffect statusEffect, bool excludeBroken, bool excludeFullCondition)
|
||||
{
|
||||
Item = item;
|
||||
StatusEffect = statusEffect;
|
||||
ExcludeBroken = excludeBroken;
|
||||
ExcludeFullCondition = excludeFullCondition;
|
||||
}
|
||||
}
|
||||
|
||||
class SlotRestrictions
|
||||
{
|
||||
public readonly int MaxStackSize;
|
||||
public readonly List<RelatedItem> ContainableItems;
|
||||
public List<RelatedItem> ContainableItems;
|
||||
|
||||
public SlotRestrictions(int maxStackSize, List<RelatedItem> containableItems)
|
||||
{
|
||||
@@ -185,7 +187,12 @@ namespace Barotrauma.Items.Components
|
||||
[Serialize(false, IsPropertySaveable.No)]
|
||||
public bool RemoveContainedItemsOnDeconstruct { get; set; }
|
||||
|
||||
private SlotRestrictions[] slotRestrictions;
|
||||
private readonly ImmutableArray<SlotRestrictions> slotRestrictions;
|
||||
|
||||
readonly List<ISerializableEntity> targets = new List<ISerializableEntity>();
|
||||
|
||||
private Vector2 prevContainedItemPositions;
|
||||
|
||||
|
||||
public bool ShouldBeContained(string[] identifiersOrTags, out bool isRestrictionsDefined)
|
||||
{
|
||||
@@ -235,10 +242,11 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
Inventory = new ItemInventory(item, this, totalCapacity, SlotsPerRow);
|
||||
slotRestrictions = new SlotRestrictions[totalCapacity];
|
||||
|
||||
List<SlotRestrictions> newSlotRestrictions = new List<SlotRestrictions>(totalCapacity);
|
||||
for (int i = 0; i < capacity; i++)
|
||||
{
|
||||
slotRestrictions[i] = new SlotRestrictions(maxStackSize, ContainableItems);
|
||||
newSlotRestrictions.Add(new SlotRestrictions(maxStackSize, ContainableItems));
|
||||
}
|
||||
|
||||
int subContainerIndex = capacity;
|
||||
@@ -266,14 +274,37 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
for (int i = subContainerIndex; i < subContainerIndex + subCapacity; i++)
|
||||
{
|
||||
slotRestrictions[i] = new SlotRestrictions(subMaxStackSize, subContainableItems);
|
||||
newSlotRestrictions.Add(new SlotRestrictions(subMaxStackSize, subContainableItems));
|
||||
}
|
||||
subContainerIndex += subCapacity;
|
||||
}
|
||||
capacity = totalCapacity;
|
||||
slotRestrictions = newSlotRestrictions.ToImmutableArray();
|
||||
System.Diagnostics.Debug.Assert(totalCapacity == slotRestrictions.Length);
|
||||
InitProjSpecific(element);
|
||||
}
|
||||
|
||||
public void ReloadContainableRestrictions(ContentXElement element)
|
||||
{
|
||||
int containableIndex = 0;
|
||||
foreach (var subElement in element.GetChildElements("containable"))
|
||||
{
|
||||
RelatedItem containable = RelatedItem.Load(subElement, returnEmpty: false, parentDebugName: item.Name);
|
||||
if (containable == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error when loading containable restrictions for \"" + item.Name + "\" - containable with no identifiers.");
|
||||
continue;
|
||||
}
|
||||
ContainableItems[containableIndex] = containable;
|
||||
containableIndex++;
|
||||
if (containableIndex >= ContainableItems.Count) { break; }
|
||||
}
|
||||
for (int i = 0; i < capacity; i++)
|
||||
{
|
||||
slotRestrictions[i].ContainableItems = ContainableItems;
|
||||
}
|
||||
}
|
||||
|
||||
public int GetMaxStackSize(int slotIndex)
|
||||
{
|
||||
if (slotIndex < 0 || slotIndex >= capacity)
|
||||
@@ -300,7 +331,7 @@ namespace Barotrauma.Items.Components
|
||||
if (!containableItem.MatchesItem(containedItem)) { continue; }
|
||||
foreach (StatusEffect effect in containableItem.statusEffects)
|
||||
{
|
||||
activeContainedItems.Add(new ActiveContainedItem(containedItem, effect, containableItem.ExcludeBroken));
|
||||
activeContainedItems.Add(new ActiveContainedItem(containedItem, effect, containableItem.ExcludeBroken, containableItem.ExcludeFullCondition));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -315,7 +346,7 @@ namespace Barotrauma.Items.Components
|
||||
IsActive = activeContainedItems.Count > 0 || Inventory.AllItems.Any(it => it.body != null);
|
||||
}
|
||||
|
||||
public override void Move(Vector2 amount)
|
||||
public override void Move(Vector2 amount, bool ignoreContacts = false)
|
||||
{
|
||||
SetContainedItemPositions();
|
||||
}
|
||||
@@ -363,18 +394,21 @@ namespace Barotrauma.Items.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
readonly List<ISerializableEntity> targets = new List<ISerializableEntity>();
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(SpawnWithId) && !alwaysContainedItemsSpawned)
|
||||
{
|
||||
SpawnAlwaysContainedItems();
|
||||
alwaysContainedItemsSpawned = true;
|
||||
}
|
||||
|
||||
if (item.ParentInventory is CharacterInventory ownerInventory)
|
||||
{
|
||||
item.SetContainedItemPositions();
|
||||
if (Vector2.DistanceSquared(prevContainedItemPositions, item.Position) > 10.0f)
|
||||
{
|
||||
SetContainedItemPositions();
|
||||
prevContainedItemPositions = item.Position;
|
||||
}
|
||||
|
||||
if (AutoInject)
|
||||
{
|
||||
@@ -395,7 +429,7 @@ namespace Barotrauma.Items.Components
|
||||
item.body.Enabled &&
|
||||
item.body.FarseerBody.Awake)
|
||||
{
|
||||
item.SetContainedItemPositions();
|
||||
SetContainedItemPositions();
|
||||
}
|
||||
else if (activeContainedItems.Count == 0)
|
||||
{
|
||||
@@ -408,6 +442,7 @@ namespace Barotrauma.Items.Components
|
||||
Item contained = activeContainedItem.Item;
|
||||
|
||||
if (activeContainedItem.ExcludeBroken && contained.Condition <= 0.0f) { continue; }
|
||||
if (activeContainedItem.ExcludeFullCondition && contained.IsFullCondition) { continue; }
|
||||
StatusEffect effect = activeContainedItem.StatusEffect;
|
||||
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.This))
|
||||
@@ -569,7 +604,7 @@ namespace Barotrauma.Items.Components
|
||||
transformedItemPos += new Vector2(item.Rect.X, item.Rect.Y);
|
||||
if (Math.Abs(item.Rotation) > 0.01f)
|
||||
{
|
||||
Matrix transform = Matrix.CreateRotationZ(MathHelper.ToRadians(-item.Rotation));
|
||||
Matrix transform = Matrix.CreateRotationZ(-item.RotationRad);
|
||||
transformedItemPos = Vector2.Transform(transformedItemPos - item.Position, transform) + item.Position;
|
||||
transformedItemInterval = Vector2.Transform(transformedItemInterval, transform);
|
||||
transformedItemIntervalHorizontal = Vector2.Transform(transformedItemIntervalHorizontal, transform);
|
||||
@@ -600,7 +635,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
currentRotation += MathHelper.ToRadians(-item.Rotation);
|
||||
currentRotation += -item.RotationRad;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
@@ -751,7 +786,11 @@ namespace Barotrauma.Items.Components
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
Inventory.AllItemsMod.ForEach(it => it.Drop(null));
|
||||
//if we're unloading the whole sub, no need to drop anything (everything's going to be removed anyway)
|
||||
if (!Submarine.Unloading)
|
||||
{
|
||||
Inventory.AllItemsMod.ForEach(it => it.Drop(null));
|
||||
}
|
||||
}
|
||||
|
||||
public override void Load(ContentXElement componentElement, bool usePrefabValues, IdRemap idRemap)
|
||||
|
||||
@@ -114,6 +114,20 @@ namespace Barotrauma.Items.Components
|
||||
private set;
|
||||
} = true;
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No)]
|
||||
public bool NonInteractableWhenFlippedX
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No)]
|
||||
public bool NonInteractableWhenFlippedY
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Controller(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
@@ -147,7 +161,7 @@ namespace Barotrauma.Items.Components
|
||||
CancelUsing(user);
|
||||
user = null;
|
||||
}
|
||||
if (!IsToggle) { IsActive = false; }
|
||||
if (!IsToggle || item.Connections == null) { IsActive = false; }
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -462,19 +476,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
dir = dir == Direction.Left ? Direction.Right : Direction.Left;
|
||||
}
|
||||
|
||||
userPos.X = -UserPos.X;
|
||||
|
||||
for (int i = 0; i < limbPositions.Count; i++)
|
||||
{
|
||||
float diff = (item.Rect.X + limbPositions[i].Position.X * item.Scale) - item.Rect.Center.X;
|
||||
|
||||
Vector2 flippedPos =
|
||||
new Vector2(
|
||||
(item.Rect.Center.X - diff - item.Rect.X) / item.Scale,
|
||||
limbPositions[i].Position.Y);
|
||||
limbPositions[i] = new LimbPos(limbPositions[i].LimbType, flippedPos, limbPositions[i].AllowUsingLimb);
|
||||
}
|
||||
userPos.X = -UserPos.X;
|
||||
FlipLimbPositions();
|
||||
}
|
||||
|
||||
public override void FlipY(bool relativeToSub)
|
||||
@@ -519,6 +522,11 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (Screen.Selected == GameMain.SubEditorScreen)
|
||||
{
|
||||
if (item.FlippedX)
|
||||
{
|
||||
FlipLimbPositions();
|
||||
}
|
||||
// Don't save flipped positions.
|
||||
foreach (var limbPos in limbPositions)
|
||||
{
|
||||
element.Add(new XElement("limbposition",
|
||||
@@ -526,6 +534,10 @@ namespace Barotrauma.Items.Components
|
||||
new XAttribute("position", XMLExtensions.Vector2ToString(limbPos.Position)),
|
||||
new XAttribute("allowusinglimb", limbPos.AllowUsingLimb)));
|
||||
}
|
||||
if (item.FlippedX)
|
||||
{
|
||||
FlipLimbPositions();
|
||||
}
|
||||
}
|
||||
return element;
|
||||
}
|
||||
@@ -558,5 +570,41 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void FlipLimbPositions()
|
||||
{
|
||||
for (int i = 0; i < limbPositions.Count; i++)
|
||||
{
|
||||
float diff = (item.Rect.X + limbPositions[i].Position.X * item.Scale) - item.Rect.Center.X;
|
||||
|
||||
Vector2 flippedPos =
|
||||
new Vector2(
|
||||
(item.Rect.Center.X - diff - item.Rect.X) / item.Scale,
|
||||
limbPositions[i].Position.Y);
|
||||
limbPositions[i] = new LimbPos(limbPositions[i].LimbType, flippedPos, limbPositions[i].AllowUsingLimb);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnItemLoaded()
|
||||
{
|
||||
if (item.FlippedX && NonInteractableWhenFlippedX)
|
||||
{
|
||||
item.NonInteractable = true;
|
||||
}
|
||||
else if (item.FlippedY && NonInteractableWhenFlippedY)
|
||||
{
|
||||
item.NonInteractable = true;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
LoadLimbPositions(originalElement);
|
||||
if (item.FlippedX)
|
||||
{
|
||||
FlipLimbPositions();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+22
-10
@@ -231,28 +231,40 @@ namespace Barotrauma.Items.Components
|
||||
if (targetItem == otherItem) { continue; }
|
||||
if (deconstructProduct.RequiredOtherItem.Any(r => otherItem.HasTag(r) || r == otherItem.Prefab.Identifier))
|
||||
{
|
||||
user?.CheckTalents(AbilityEffectType.OnGeneticMaterialCombinedOrRefined);
|
||||
foreach (Character character in Character.GetFriendlyCrew(user))
|
||||
{
|
||||
character.CheckTalents(AbilityEffectType.OnCrewGeneticMaterialCombinedOrRefined);
|
||||
}
|
||||
|
||||
var geneticMaterial1 = targetItem.GetComponent<GeneticMaterial>();
|
||||
var geneticMaterial2 = otherItem.GetComponent<GeneticMaterial>();
|
||||
if (geneticMaterial1 != null && geneticMaterial2 != null)
|
||||
{
|
||||
if (geneticMaterial1.Combine(geneticMaterial2, user))
|
||||
var result = geneticMaterial1.Combine(geneticMaterial2, user);
|
||||
if (result == GeneticMaterial.CombineResult.Refined)
|
||||
{
|
||||
inputContainer.Inventory.RemoveItem(otherItem);
|
||||
OutputContainer.Inventory.RemoveItem(otherItem);
|
||||
Entity.Spawner.AddItemToRemoveQueue(otherItem);
|
||||
}
|
||||
if (result != GeneticMaterial.CombineResult.None)
|
||||
{
|
||||
OnCombinedOrRefined();
|
||||
}
|
||||
allowRemove = false;
|
||||
return;
|
||||
}
|
||||
inputContainer.Inventory.RemoveItem(otherItem);
|
||||
OutputContainer.Inventory.RemoveItem(otherItem);
|
||||
Entity.Spawner.AddItemToRemoveQueue(otherItem);
|
||||
else
|
||||
{
|
||||
inputContainer.Inventory.RemoveItem(otherItem);
|
||||
OutputContainer.Inventory.RemoveItem(otherItem);
|
||||
Entity.Spawner.AddItemToRemoveQueue(otherItem);
|
||||
OnCombinedOrRefined();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnCombinedOrRefined()
|
||||
{
|
||||
user?.CheckTalents(AbilityEffectType.OnGeneticMaterialCombinedOrRefined);
|
||||
foreach (Character character in Character.GetFriendlyCrew(user))
|
||||
{
|
||||
character.CheckTalents(AbilityEffectType.OnCrewGeneticMaterialCombinedOrRefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ namespace Barotrauma.Items.Components
|
||||
hullData.ReceivedWaterAmount = null;
|
||||
if (fromWaterDetector)
|
||||
{
|
||||
hullData.ReceivedWaterAmount = Math.Min(sourceHull.WaterVolume / sourceHull.Volume, 1.0f);
|
||||
hullData.ReceivedWaterAmount = WaterDetector.GetWaterPercentage(sourceHull);
|
||||
}
|
||||
foreach (var linked in sourceHull.linkedTo)
|
||||
{
|
||||
@@ -174,7 +174,7 @@ namespace Barotrauma.Items.Components
|
||||
linkedHullData.ReceivedWaterAmount = null;
|
||||
if (fromWaterDetector)
|
||||
{
|
||||
linkedHullData.ReceivedWaterAmount = Math.Min(linkedHull.WaterVolume / linkedHull.Volume, 1.0f);
|
||||
linkedHullData.ReceivedWaterAmount = WaterDetector.GetWaterPercentage(linkedHull);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.MapCreatures.Behavior;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.MapCreatures.Behavior;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -24,7 +23,10 @@ namespace Barotrauma.Items.Components
|
||||
if (value == hijacked) { return; }
|
||||
hijacked = value;
|
||||
#if SERVER
|
||||
item.CreateServerEvent(this);
|
||||
if (!Submarine.Unloading)
|
||||
{
|
||||
item.CreateServerEvent(this);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +68,8 @@ namespace Barotrauma.Items.Components
|
||||
private const float ConnectedSubUpdateInterval = 1.0f;
|
||||
float connectedSubUpdateTimer;
|
||||
|
||||
private double lastReceivedSteeringSignalTime;
|
||||
|
||||
public bool AutoPilot
|
||||
{
|
||||
get { return autoPilot; }
|
||||
@@ -312,16 +314,20 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else if (AutoPilot)
|
||||
{
|
||||
UpdateAutoPilot(deltaTime);
|
||||
float throttle = 1.0f;
|
||||
if (controlledSub != null)
|
||||
//signals override autopilot for a duration of one second
|
||||
if (lastReceivedSteeringSignalTime < Timing.TotalTime - 1)
|
||||
{
|
||||
//if the sub is heading in the correct direction, throttle the speed according to the user's skill
|
||||
//if it's e.g. sinking due to extra water, don't throttle, but allow emptying up the ballast completely
|
||||
throttle = MathHelper.Clamp(Vector2.Dot(controlledSub.Velocity, TargetVelocity) / 100.0f, 0.0f, 1.0f);
|
||||
UpdateAutoPilot(deltaTime);
|
||||
float throttle = 1.0f;
|
||||
if (controlledSub != null)
|
||||
{
|
||||
//if the sub is heading in the correct direction, throttle the speed according to the user's skill
|
||||
//if it's e.g. sinking due to extra water, don't throttle, but allow emptying up the ballast completely
|
||||
throttle = MathHelper.Clamp(Vector2.Dot(controlledSub.Velocity, TargetVelocity) / 100.0f, 0.0f, 1.0f);
|
||||
}
|
||||
float maxSpeed = MathHelper.Lerp(AutoPilotMaxSpeed, AIPilotMaxSpeed, userSkill) * 100.0f;
|
||||
TargetVelocity = TargetVelocity.ClampLength(MathHelper.Lerp(100.0f, maxSpeed, throttle));
|
||||
}
|
||||
float maxSpeed = MathHelper.Lerp(AutoPilotMaxSpeed, AIPilotMaxSpeed, userSkill) * 100.0f;
|
||||
TargetVelocity = TargetVelocity.ClampLength(MathHelper.Lerp(100.0f, maxSpeed, throttle));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -394,8 +400,8 @@ namespace Barotrauma.Items.Components
|
||||
private void IncreaseSkillLevel(Character user, float deltaTime)
|
||||
{
|
||||
if (user?.Info == null) { return; }
|
||||
// Do not increase the helm skill when "steering" the sub in an outpost level
|
||||
if (GameMain.GameSession?.Campaign != null && Level.IsLoadedOutpost) { return; }
|
||||
// Do not increase the helm skill when "steering" the sub while docked into something static (e.g. outpost or wreck)
|
||||
if (GameMain.GameSession?.Campaign != null && controlledSub != null && controlledSub.DockedTo.Any(d => d.PhysicsBody.BodyType == BodyType.Static)) { return; }
|
||||
|
||||
float userSkill = Math.Max(user.GetSkillLevel("helm"), 1.0f) / 100.0f;
|
||||
user.Info.IncreaseSkillLevel(
|
||||
@@ -821,6 +827,7 @@ namespace Barotrauma.Items.Components
|
||||
steeringInput.X = MathHelper.Clamp(steeringInput.X, -100.0f, 100.0f);
|
||||
steeringInput.Y = MathHelper.Clamp(-steeringInput.Y, -100.0f, 100.0f);
|
||||
TargetVelocity = steeringInput;
|
||||
lastReceivedSteeringSignalTime = Timing.TotalTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -3,7 +3,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
@@ -81,6 +80,8 @@ namespace Barotrauma.Items.Components
|
||||
private ItemContainer? container;
|
||||
private float growthTickTimer;
|
||||
|
||||
private List<LightComponent>? lightComponents;
|
||||
|
||||
public Planter(Item item, ContentXElement element) : base(item, element)
|
||||
{
|
||||
canBePicked = true;
|
||||
@@ -107,10 +108,14 @@ namespace Barotrauma.Items.Components
|
||||
base.OnItemLoaded();
|
||||
IsActive = true;
|
||||
#if CLIENT
|
||||
lightComponent = item.GetComponent<LightComponent>();
|
||||
if (lightComponent != null)
|
||||
var lights = item.GetComponents<LightComponent>();
|
||||
if (lights.Any())
|
||||
{
|
||||
lightComponent.Light.Enabled = false;
|
||||
lightComponents = lights.ToList();
|
||||
foreach (var light in lightComponents)
|
||||
{
|
||||
light.Light.Enabled = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
container = item.GetComponent<ItemContainer>();
|
||||
@@ -227,12 +232,17 @@ namespace Barotrauma.Items.Components
|
||||
base.Update(deltaTime, cam);
|
||||
|
||||
#if CLIENT
|
||||
if (lightComponent != null)
|
||||
if (lightComponents != null && lightComponents.Count > 0)
|
||||
{
|
||||
bool hasSeed = false;
|
||||
foreach (Growable? seed in GrowableSeeds) { hasSeed |= seed != null; }
|
||||
|
||||
lightComponent.Light.Enabled = hasSeed;
|
||||
foreach (Growable? seed in GrowableSeeds)
|
||||
{
|
||||
hasSeed |= seed != null;
|
||||
}
|
||||
foreach (var light in lightComponents)
|
||||
{
|
||||
light.Light.Enabled = hasSeed;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace Barotrauma.Items.Components
|
||||
get { return poweredList; }
|
||||
}
|
||||
|
||||
public static readonly List<Connection> ChangedConnections = new List<Connection>();
|
||||
public static readonly HashSet<Connection> ChangedConnections = new HashSet<Connection>();
|
||||
|
||||
public readonly static Dictionary<int, GridInfo> Grids = new Dictionary<int, GridInfo>();
|
||||
|
||||
@@ -150,7 +150,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (powerOut?.Grid != null) { return powerOut.Grid.Voltage; }
|
||||
}
|
||||
return voltage;
|
||||
return PowerConsumption <= 0.0f ? 1.0f : voltage;
|
||||
}
|
||||
set
|
||||
{
|
||||
@@ -158,21 +158,13 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public bool PoweredByTinkering
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this is PowerContainer) { return false; }
|
||||
foreach (Repairable repairable in Item.Repairables)
|
||||
{
|
||||
if (repairable.IsTinkering && repairable.TinkeringPowersDevices)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Essentially Voltage / MinVoltage (= how much of the minimum required voltage has been satisfied), clamped between 0 and 1.
|
||||
/// Can be used by status effects or sounds to check if the item has enough power to run
|
||||
/// </summary>
|
||||
public float RelativeVoltage => minVoltage <= 0.0f ? 1.0f : MathHelper.Clamp(Voltage / minVoltage, 0.0f, 1.0f);
|
||||
|
||||
public bool PoweredByTinkering { get; set; }
|
||||
|
||||
[Editable, Serialize(true, IsPropertySaveable.Yes, description: "Can the item be damaged by electomagnetic pulses.")]
|
||||
public bool VulnerableToEMP
|
||||
@@ -449,7 +441,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
#if CLIENT
|
||||
sw.Stop();
|
||||
GameMain.PerformanceCounter.AddElapsedTicks("GridUpdate", sw.ElapsedTicks);
|
||||
GameMain.PerformanceCounter.AddElapsedTicks("Update:Power", sw.ElapsedTicks);
|
||||
sw.Restart();
|
||||
#endif
|
||||
|
||||
@@ -606,7 +598,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
#if CLIENT
|
||||
sw.Stop();
|
||||
GameMain.PerformanceCounter.AddElapsedTicks("PowerUpdate", sw.ElapsedTicks);
|
||||
GameMain.PerformanceCounter.AddElapsedTicks("Update:Power", sw.ElapsedTicks);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -231,6 +231,13 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No, description:"Enable only if you want to make the projectile ignore collisions with other projectiles when it's shot. Doesn't have any effect, if the item is not set to be damaged by projectiles.")]
|
||||
public bool IgnoreProjectilesWhileActive
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Body StickTarget
|
||||
{
|
||||
get;
|
||||
@@ -405,6 +412,10 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
item.body.CollisionCategories = Physics.CollisionProjectile;
|
||||
item.body.CollidesWith = Physics.CollisionCharacter | Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionItemBlocking;
|
||||
if (item.Prefab.DamagedByProjectiles && !IgnoreProjectilesWhileActive)
|
||||
{
|
||||
item.body.CollidesWith |= Physics.CollisionProjectile;
|
||||
}
|
||||
|
||||
IsActive = true;
|
||||
|
||||
@@ -835,6 +846,10 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else if (target.Body.UserData is Limb limb)
|
||||
{
|
||||
if (!FriendlyFire && User != null && limb.character.IsFriendly(User))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// when hitting limbs with piercing ammo, don't lose as much speed
|
||||
if (MaxTargetsToHit > 1)
|
||||
{
|
||||
@@ -954,7 +969,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
item.body.LinearVelocity *= deflectedSpeedMultiplier;
|
||||
}
|
||||
else if ( stickJoint == null && StickTarget == null &&
|
||||
else if ( remainingHits <= 0 &&
|
||||
stickJoint == null && StickTarget == null &&
|
||||
StickToStructures && target.Body.UserData is Structure ||
|
||||
((StickToLightTargets || target.Body.Mass > item.body.Mass * 0.5f) &&
|
||||
(DoesStick ||
|
||||
|
||||
@@ -56,7 +56,8 @@ namespace Barotrauma.Items.Components
|
||||
if (value == qualityLevel) { return; }
|
||||
|
||||
bool wasInFullCondition = item.IsFullCondition;
|
||||
qualityLevel = MathHelper.Clamp(value, 0, MaxQuality);
|
||||
qualityLevel = MathHelper.Clamp(value, 0, MaxQuality);
|
||||
item.RecalculateConditionValues();
|
||||
//set the condition to the new max condition
|
||||
if (wasInFullCondition && statValues.ContainsKey(StatType.Condition))
|
||||
{
|
||||
|
||||
@@ -106,7 +106,25 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsTinkering { get; private set; } = false;
|
||||
private bool isTinkering;
|
||||
public bool IsTinkering
|
||||
{
|
||||
get { return isTinkering; }
|
||||
private set
|
||||
{
|
||||
if (isTinkering == value) { return; }
|
||||
isTinkering = value;
|
||||
|
||||
if (tinkeringPowersDevices)
|
||||
{
|
||||
foreach (Powered powered in item.GetComponents<Powered>())
|
||||
{
|
||||
if (powered is PowerContainer) { continue; }
|
||||
powered.PoweredByTinkering = isTinkering;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Character CurrentFixer { get; private set; }
|
||||
private Item currentRepairItem;
|
||||
@@ -584,7 +602,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private bool ShouldDeteriorate()
|
||||
{
|
||||
if (Level.IsLoadedOutpost) { return false; }
|
||||
if (Level.IsLoadedFriendlyOutpost) { return false; }
|
||||
|
||||
if (LastActiveTime > Timing.TotalTime) { return true; }
|
||||
foreach (ItemComponent ic in item.Components)
|
||||
|
||||
@@ -160,7 +160,8 @@ namespace Barotrauma.Items.Components
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (source == null || target == null || target.Removed ||
|
||||
(source is Entity sourceEntity && sourceEntity.Removed))
|
||||
(source is Entity sourceEntity && sourceEntity.Removed) ||
|
||||
(source is Limb limb && limb.Removed))
|
||||
{
|
||||
ResetSource();
|
||||
target = null;
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
sealed class AndComponent : BooleanOperatorComponent
|
||||
{
|
||||
public AndComponent(Item item, ContentXElement element)
|
||||
: base(item, element) { }
|
||||
|
||||
protected override bool GetOutput(int numTrueInputs) => numTrueInputs >= 2;
|
||||
}
|
||||
}
|
||||
+8
-5
@@ -3,7 +3,7 @@ using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class AndComponent : ItemComponent
|
||||
abstract class BooleanOperatorComponent : ItemComponent
|
||||
{
|
||||
protected string output, falseOutput;
|
||||
|
||||
@@ -70,22 +70,25 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public AndComponent(Item item, ContentXElement element)
|
||||
public BooleanOperatorComponent(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
timeSinceReceived = new float[] { Math.Max(timeFrame * 2.0f, 0.1f), Math.Max(timeFrame * 2.0f, 0.1f) };
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
protected abstract bool GetOutput(int numTrueInputs);
|
||||
|
||||
public sealed override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
bool state = true;
|
||||
int receivedInputs = 0;
|
||||
for (int i = 0; i < timeSinceReceived.Length; i++)
|
||||
{
|
||||
if (timeSinceReceived[i] > timeFrame) { state = false; }
|
||||
if (timeSinceReceived[i] <= timeFrame) { receivedInputs += 1; }
|
||||
timeSinceReceived[i] += deltaTime;
|
||||
}
|
||||
|
||||
bool state = GetOutput(receivedInputs);
|
||||
string signalOut = state ? output : falseOutput;
|
||||
if (string.IsNullOrEmpty(signalOut))
|
||||
{
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
sealed class OrComponent : BooleanOperatorComponent
|
||||
{
|
||||
public OrComponent(Item item, ContentXElement element)
|
||||
: base(item, element) { }
|
||||
|
||||
protected override bool GetOutput(int numTrueInputs) => numTrueInputs > 0;
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
sealed class XorComponent : BooleanOperatorComponent
|
||||
{
|
||||
public XorComponent(Item item, ContentXElement element)
|
||||
: base(item, element) { }
|
||||
|
||||
protected override bool GetOutput(int numTrueInputs) => numTrueInputs == 1;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
@@ -19,11 +20,8 @@ namespace Barotrauma.Items.Components
|
||||
public readonly string Name;
|
||||
public readonly LocalizedString DisplayName;
|
||||
|
||||
private readonly Wire[] wires;
|
||||
public IEnumerable<Wire> Wires
|
||||
{
|
||||
get { return wires; }
|
||||
}
|
||||
private readonly HashSet<Wire> wires;
|
||||
public IReadOnlyCollection<Wire> Wires => wires;
|
||||
|
||||
private readonly Item item;
|
||||
|
||||
@@ -31,7 +29,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public readonly List<StatusEffect> Effects;
|
||||
|
||||
public readonly ushort[] wireId;
|
||||
public readonly List<ushort> LoadedWireIds;
|
||||
|
||||
//The grid the connection is a part of
|
||||
public GridInfo Grid;
|
||||
@@ -92,7 +90,7 @@ namespace Barotrauma.Items.Components
|
||||
MaxWires = Math.Max(element.Elements().Count(e => e.Name.ToString().Equals("link", StringComparison.OrdinalIgnoreCase)), MaxWires);
|
||||
|
||||
MaxPlayerConnectableWires = element.GetAttributeInt("maxplayerconnectablewires", MaxWires);
|
||||
wires = new Wire[MaxWires];
|
||||
wires = new HashSet<Wire>();
|
||||
|
||||
IsOutput = element.Name.ToString() == "output";
|
||||
Name = element.GetAttributeString("name", IsOutput ? "output" : "input");
|
||||
@@ -150,23 +148,15 @@ namespace Barotrauma.Items.Components
|
||||
IsPower = Name == "power_in" || Name == "power" || Name == "power_out";
|
||||
|
||||
|
||||
wireId = new ushort[MaxWires];
|
||||
|
||||
LoadedWireIds = new List<ushort>();
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "link":
|
||||
int index = -1;
|
||||
for (int i = 0; i < MaxWires; i++)
|
||||
{
|
||||
if (wireId[i] < 1) { index = i; }
|
||||
}
|
||||
if (index == -1) { break; }
|
||||
|
||||
int id = subElement.GetAttributeInt("w", 0);
|
||||
if (id < 0) { id = 0; }
|
||||
wireId[index] = idRemap.GetOffsetId(id);
|
||||
if (LoadedWireIds.Count < MaxWires) { LoadedWireIds.Add(idRemap.GetOffsetId(id)); }
|
||||
|
||||
break;
|
||||
case "statuseffect":
|
||||
@@ -180,143 +170,117 @@ namespace Barotrauma.Items.Components
|
||||
public void SetRecipientsDirty()
|
||||
{
|
||||
recipientsDirty = true;
|
||||
if (IsPower) { Powered.ChangedConnections.Add(this); }
|
||||
}
|
||||
|
||||
private void RefreshRecipients()
|
||||
{
|
||||
recipients.Clear();
|
||||
for (int i = 0; i < MaxWires; i++)
|
||||
foreach (var wire in wires)
|
||||
{
|
||||
if (wires[i] == null) continue;
|
||||
Connection recipient = wires[i].OtherConnection(this);
|
||||
if (recipient != null) recipients.Add(recipient);
|
||||
Connection recipient = wire.OtherConnection(this);
|
||||
if (recipient != null) { recipients.Add(recipient); }
|
||||
}
|
||||
recipientsDirty = false;
|
||||
}
|
||||
|
||||
public int FindEmptyIndex()
|
||||
{
|
||||
for (int i = 0; i < MaxWires; i++)
|
||||
{
|
||||
if (wires[i] == null) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int FindWireIndex(Wire wire)
|
||||
{
|
||||
for (int i = 0; i < MaxWires; i++)
|
||||
{
|
||||
if (wires[i] == wire) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int FindWireIndex(Item wireItem)
|
||||
{
|
||||
for (int i = 0; i < MaxWires; i++)
|
||||
{
|
||||
if (wires[i] == null && wireItem == null) return i;
|
||||
if (wires[i] != null && wires[i].Item == wireItem) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
public Wire FindWireByItem(Item it)
|
||||
=> Wires.FirstOrDefault(w => w.Item == it);
|
||||
|
||||
public bool WireSlotsAvailable()
|
||||
=> wires.Count < MaxWires;
|
||||
|
||||
public bool TryAddLink(Wire wire)
|
||||
{
|
||||
for (int i = 0; i < MaxWires; i++)
|
||||
if (wire is null
|
||||
|| wires.Contains(wire)
|
||||
|| !WireSlotsAvailable())
|
||||
{
|
||||
if (wires[i] == null)
|
||||
{
|
||||
SetWire(i, wire);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
wires.Add(wire);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SetWire(int index, Wire wire)
|
||||
public void DisconnectWire(Wire wire)
|
||||
{
|
||||
Wire previousWire = wires[index];
|
||||
if (wire != previousWire && previousWire != null)
|
||||
{
|
||||
var otherConnection = previousWire.OtherConnection(this);
|
||||
if (otherConnection != null)
|
||||
{
|
||||
//Change the connection grids or flag them for updating
|
||||
if (IsPower && otherConnection.IsPower && Grid != null)
|
||||
{
|
||||
//Check if both connections belong to a larger grid
|
||||
if (otherConnection.recipients.Count > 1 && recipients.Count > 1)
|
||||
{
|
||||
Powered.ChangedConnections.Add(otherConnection);
|
||||
Powered.ChangedConnections.Add(this);
|
||||
}
|
||||
else if (recipients.Count > 1)
|
||||
{
|
||||
//This wire was the only one at the other grid
|
||||
otherConnection.Grid?.RemoveConnection(otherConnection);
|
||||
otherConnection.Grid = null;
|
||||
}
|
||||
else if (otherConnection.recipients.Count > 1)
|
||||
{
|
||||
Grid?.RemoveConnection(this);
|
||||
Grid = null;
|
||||
}
|
||||
else if (Grid.Connections.Count == 2)
|
||||
{
|
||||
//Delete the grid as these were the only 2 devices
|
||||
Powered.Grids.Remove(Grid.ID);
|
||||
Grid = null;
|
||||
otherConnection.Grid = null;
|
||||
}
|
||||
}
|
||||
otherConnection.recipientsDirty = true;
|
||||
}
|
||||
}
|
||||
if (wire == null || !wires.Contains(wire)) { return; }
|
||||
|
||||
wires[index] = wire;
|
||||
var prevOtherConnection = wire.OtherConnection(this);
|
||||
if (prevOtherConnection != null)
|
||||
{
|
||||
//Change the connection grids or flag them for updating
|
||||
if (IsPower && prevOtherConnection.IsPower && Grid != null)
|
||||
{
|
||||
//Check if both connections belong to a larger grid
|
||||
if (prevOtherConnection.recipients.Count > 1 && recipients.Count > 1)
|
||||
{
|
||||
Powered.ChangedConnections.Add(prevOtherConnection);
|
||||
Powered.ChangedConnections.Add(this);
|
||||
}
|
||||
else if (recipients.Count > 1)
|
||||
{
|
||||
//This wire was the only one at the other grid
|
||||
prevOtherConnection.Grid?.RemoveConnection(prevOtherConnection);
|
||||
prevOtherConnection.Grid = null;
|
||||
}
|
||||
else if (prevOtherConnection.recipients.Count > 1)
|
||||
{
|
||||
Grid?.RemoveConnection(this);
|
||||
Grid = null;
|
||||
}
|
||||
else if (Grid.Connections.Count == 2)
|
||||
{
|
||||
//Delete the grid as these were the only 2 devices
|
||||
Powered.Grids.Remove(Grid.ID);
|
||||
Grid = null;
|
||||
prevOtherConnection.Grid = null;
|
||||
}
|
||||
}
|
||||
prevOtherConnection.recipientsDirty = true;
|
||||
}
|
||||
wires.Remove(wire);
|
||||
recipientsDirty = true;
|
||||
if (wire != null)
|
||||
}
|
||||
|
||||
public void ConnectWire(Wire wire)
|
||||
{
|
||||
if (wire == null || !TryAddLink(wire)) { return; }
|
||||
ConnectionPanel.DisconnectedWires.Remove(wire);
|
||||
var otherConnection = wire.OtherConnection(this);
|
||||
if (otherConnection != null)
|
||||
{
|
||||
|
||||
ConnectionPanel.DisconnectedWires.Remove(wire);
|
||||
var otherConnection = wire.OtherConnection(this);
|
||||
if (otherConnection != null)
|
||||
//Set the other connection grid if a grid exists already
|
||||
if (Powered.ValidPowerConnection(this, otherConnection))
|
||||
{
|
||||
//Set the other connection grid if a grid exists already
|
||||
if (Powered.ValidPowerConnection(this, otherConnection))
|
||||
if (Grid == null && otherConnection.Grid != null)
|
||||
{
|
||||
if (Grid == null && otherConnection.Grid != null)
|
||||
{
|
||||
otherConnection.Grid.AddConnection(this);
|
||||
Grid = otherConnection.Grid;
|
||||
}
|
||||
else if (Grid != null && otherConnection.Grid == null)
|
||||
{
|
||||
Grid.AddConnection(otherConnection);
|
||||
otherConnection.Grid = Grid;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Flag change so that proper grids can be formed
|
||||
Powered.ChangedConnections.Add(this);
|
||||
Powered.ChangedConnections.Add(otherConnection);
|
||||
}
|
||||
otherConnection.Grid.AddConnection(this);
|
||||
Grid = otherConnection.Grid;
|
||||
}
|
||||
else if (Grid != null && otherConnection.Grid == null)
|
||||
{
|
||||
Grid.AddConnection(otherConnection);
|
||||
otherConnection.Grid = Grid;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Flag change so that proper grids can be formed
|
||||
Powered.ChangedConnections.Add(this);
|
||||
Powered.ChangedConnections.Add(otherConnection);
|
||||
}
|
||||
|
||||
otherConnection.recipientsDirty = true;
|
||||
}
|
||||
|
||||
otherConnection.recipientsDirty = true;
|
||||
}
|
||||
recipientsDirty = true;
|
||||
}
|
||||
|
||||
public void SendSignal(Signal signal)
|
||||
{
|
||||
for (int i = 0; i < MaxWires; i++)
|
||||
foreach (var wire in wires)
|
||||
{
|
||||
if (wires[i] == null) { continue; }
|
||||
|
||||
Connection recipient = wires[i].OtherConnection(this);
|
||||
Connection recipient = wire.OtherConnection(this);
|
||||
if (recipient == null) { continue; }
|
||||
if (recipient.item == this.item || signal.source?.LastSentSignalRecipients.LastOrDefault() == recipient) { continue; }
|
||||
|
||||
@@ -354,35 +318,32 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < MaxWires; i++)
|
||||
foreach (var wire in wires)
|
||||
{
|
||||
if (wires[i] == null) continue;
|
||||
|
||||
wires[i].RemoveConnection(this);
|
||||
wires[i] = null;
|
||||
wire.RemoveConnection(this);
|
||||
recipientsDirty = true;
|
||||
}
|
||||
wires.Clear();
|
||||
}
|
||||
|
||||
public void ConnectLinked()
|
||||
public void InitializeFromLoaded()
|
||||
{
|
||||
if (wireId == null) return;
|
||||
if (LoadedWireIds.Count == 0) { return; }
|
||||
|
||||
for (int i = 0; i < MaxWires; i++)
|
||||
for (int i = 0; i < LoadedWireIds.Count; i++)
|
||||
{
|
||||
if (wireId[i] == 0) { continue; }
|
||||
if (!(Entity.FindEntityByID(LoadedWireIds[i]) is Item wireItem)) { continue; }
|
||||
|
||||
if (!(Entity.FindEntityByID(wireId[i]) is Item wireItem)) { continue; }
|
||||
wires[i] = wireItem.GetComponent<Wire>();
|
||||
recipientsDirty = true;
|
||||
|
||||
if (wires[i] != null)
|
||||
var wire = wireItem.GetComponent<Wire>();
|
||||
if (wire != null && TryAddLink(wire))
|
||||
{
|
||||
if (wires[i].Item.body != null) wires[i].Item.body.Enabled = false;
|
||||
wires[i].Connect(this, false, false);
|
||||
wires[i].FixNodeEnds();
|
||||
if (wire.Item.body != null) wire.Item.body.Enabled = false;
|
||||
wire.Connect(this, false, false);
|
||||
wire.FixNodeEnds();
|
||||
recipientsDirty = true;
|
||||
}
|
||||
}
|
||||
LoadedWireIds.Clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -390,19 +351,10 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
XElement newElement = new XElement(IsOutput ? "output" : "input", new XAttribute("name", Name));
|
||||
|
||||
Array.Sort(wires, delegate (Wire wire1, Wire wire2)
|
||||
foreach (var wire in wires.OrderBy(w => w.Item.ID))
|
||||
{
|
||||
if (wire1 == null) return 1;
|
||||
if (wire2 == null) return -1;
|
||||
return wire1.Item.ID.CompareTo(wire2.Item.ID);
|
||||
});
|
||||
|
||||
for (int i = 0; i < MaxWires; i++)
|
||||
{
|
||||
if (wires[i] == null) continue;
|
||||
|
||||
newElement.Add(new XElement("link",
|
||||
new XAttribute("w", wires[i].Item.ID.ToString())));
|
||||
new XAttribute("w", wire.Item.ID.ToString())));
|
||||
}
|
||||
|
||||
parentElement.Add(newElement);
|
||||
|
||||
+7
-25
@@ -49,7 +49,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public bool TemporarilyLocked
|
||||
{
|
||||
get { return Level.IsLoadedOutpost && item.GetComponent<DockingPort>() != null; }
|
||||
get { return Level.IsLoadedOutpost && (item.GetComponent<DockingPort>()?.Docked ?? false); }
|
||||
}
|
||||
|
||||
//connection panels can't be deactivated externally (by signals or status effects)
|
||||
@@ -99,7 +99,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
foreach (Connection c in Connections)
|
||||
{
|
||||
c.ConnectLinked();
|
||||
c.InitializeFromLoaded();
|
||||
}
|
||||
|
||||
if (disconnectedWireIds != null)
|
||||
@@ -286,25 +286,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
for (int i = 0; i < loadedConnections.Count && i < Connections.Count; i++)
|
||||
{
|
||||
if (loadedConnections[i].wireId.Length == Connections[i].wireId.Length)
|
||||
{
|
||||
loadedConnections[i].wireId.CopyTo(Connections[i].wireId, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
//backwards compatibility when maximum number of wires has changed
|
||||
foreach (ushort id in loadedConnections[i].wireId)
|
||||
{
|
||||
for (int j = 0; j < Connections[i].wireId.Length; j++)
|
||||
{
|
||||
if (Connections[i].wireId[j] == 0)
|
||||
{
|
||||
Connections[i].wireId[j] = id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Connections[i].LoadedWireIds.Clear();
|
||||
Connections[i].LoadedWireIds.AddRange(loadedConnections[i].LoadedWireIds);
|
||||
}
|
||||
|
||||
disconnectedWireIds = element.GetAttributeUshortArray("disconnectedwires", Array.Empty<ushort>()).ToList();
|
||||
@@ -361,10 +344,8 @@ namespace Barotrauma.Items.Components
|
||||
DisconnectedWires.Clear();
|
||||
foreach (Connection c in Connections)
|
||||
{
|
||||
foreach (Wire wire in c.Wires)
|
||||
foreach (Wire wire in c.Wires.ToArray())
|
||||
{
|
||||
if (wire == null) { continue; }
|
||||
|
||||
if (wire.OtherConnection(c) == null) //wire not connected to anything else
|
||||
{
|
||||
#if CLIENT
|
||||
@@ -408,13 +389,14 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
foreach (Connection connection in Connections)
|
||||
{
|
||||
msg.WriteVariableUInt32((uint)connection.Wires.Count);
|
||||
foreach (Wire wire in connection.Wires)
|
||||
{
|
||||
msg.Write(wire?.Item == null ? (ushort)0 : wire.Item.ID);
|
||||
}
|
||||
}
|
||||
|
||||
msg.Write((ushort)DisconnectedWires.Count());
|
||||
msg.Write((ushort)DisconnectedWires.Count);
|
||||
foreach (Wire disconnectedWire in DisconnectedWires)
|
||||
{
|
||||
msg.Write(disconnectedWire.Item.ID);
|
||||
|
||||
+60
-8
@@ -3,6 +3,7 @@ using Barotrauma.Networking;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -34,13 +35,17 @@ namespace Barotrauma.Items.Components
|
||||
public Identifier PropertyName { get; }
|
||||
public bool TargetOnlyParentProperty { get; }
|
||||
|
||||
public int NumberInputMin { get; }
|
||||
public int NumberInputMax { get; }
|
||||
public string NumberInputMin { get; }
|
||||
public string NumberInputMax { get; }
|
||||
public string NumberInputStep { get; }
|
||||
public int NumberInputDecimalPlaces { get; }
|
||||
|
||||
public int MaxTextLength { get; }
|
||||
|
||||
public const int DefaultNumberInputMin = 0, DefaultNumberInputMax = 99;
|
||||
public bool IsIntegerInput { get; }
|
||||
public const string DefaultNumberInputMin = "0", DefaultNumberInputMax = "99", DefaultNumberInputStep = "1";
|
||||
public const int DefaultNumberInputDecimalPlaces = 0;
|
||||
public bool IsNumberInput { get; }
|
||||
public NumberType? NumberType { get; }
|
||||
public bool HasPropertyName { get; }
|
||||
public bool ShouldSetProperty { get; set; }
|
||||
|
||||
@@ -60,11 +65,34 @@ namespace Barotrauma.Items.Components
|
||||
ConnectionName = element.GetAttributeString("connection", "");
|
||||
PropertyName = element.GetAttributeIdentifier("propertyname", "");
|
||||
TargetOnlyParentProperty = element.GetAttributeBool("targetonlyparentproperty", false);
|
||||
NumberInputMin = element.GetAttributeInt("min", DefaultNumberInputMin);
|
||||
NumberInputMax = element.GetAttributeInt("max", DefaultNumberInputMax);
|
||||
NumberInputMin = element.GetAttributeString("min", DefaultNumberInputMin);
|
||||
NumberInputMax = element.GetAttributeString("max", DefaultNumberInputMax);
|
||||
NumberInputStep = element.GetAttributeString("step", DefaultNumberInputStep);
|
||||
NumberInputDecimalPlaces = element.GetAttributeInt("decimalplaces", DefaultNumberInputDecimalPlaces);
|
||||
MaxTextLength = element.GetAttributeInt("maxtextlength", int.MaxValue);
|
||||
|
||||
HasPropertyName = !PropertyName.IsEmpty;
|
||||
IsIntegerInput = HasPropertyName && element.Name.ToString().ToLowerInvariant() == "integerinput";
|
||||
if (HasPropertyName)
|
||||
{
|
||||
string elementName = element.Name.ToString().ToLowerInvariant();
|
||||
IsNumberInput = elementName == "numberinput" || elementName == "integerinput"; // backwards compatibility
|
||||
if (IsNumberInput)
|
||||
{
|
||||
string numberType = element.GetAttributeString("numbertype", string.Empty);
|
||||
switch (numberType)
|
||||
{
|
||||
case "f":
|
||||
case "float":
|
||||
NumberType = Barotrauma.NumberType.Float;
|
||||
break;
|
||||
case "int":
|
||||
case "integer":
|
||||
default: // backwards compatibility
|
||||
NumberType = Barotrauma.NumberType.Int;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (element.GetAttribute("signal") is XAttribute attribute)
|
||||
{
|
||||
@@ -152,7 +180,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
case "button":
|
||||
case "textbox":
|
||||
case "integerinput":
|
||||
case "integerinput": // backwards compatibility
|
||||
case "numberinput":
|
||||
var button = new CustomInterfaceElement(item, subElement, this)
|
||||
{
|
||||
ContinuousSignal = false
|
||||
@@ -317,6 +346,24 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
private void ValueChanged(CustomInterfaceElement numberInputElement, float value)
|
||||
{
|
||||
if (numberInputElement == null) { return; }
|
||||
numberInputElement.Signal = value.ToString();
|
||||
if (!numberInputElement.TargetOnlyParentProperty)
|
||||
{
|
||||
foreach (ISerializableEntity e in item.AllPropertyObjects)
|
||||
{
|
||||
if (!e.SerializableProperties.ContainsKey(numberInputElement.PropertyName)) { continue; }
|
||||
e.SerializableProperties[numberInputElement.PropertyName].TrySetValue(e, value);
|
||||
}
|
||||
}
|
||||
else if (SerializableProperties.ContainsKey(numberInputElement.PropertyName))
|
||||
{
|
||||
SerializableProperties[numberInputElement.PropertyName].TrySetValue(this, value);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
foreach (CustomInterfaceElement ciElement in customInterfaceElementList)
|
||||
@@ -341,5 +388,10 @@ namespace Barotrauma.Items.Components
|
||||
signals = customInterfaceElementList.Select(ci => ci.Signal).ToArray();
|
||||
return base.Save(parentElement);
|
||||
}
|
||||
|
||||
private static bool TryParseFloatInvariantCulture(string s, out float f)
|
||||
{
|
||||
return float.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -187,7 +187,7 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
public override void Move(Vector2 amount)
|
||||
public override void Move(Vector2 amount, bool ignoreContacts = false)
|
||||
{
|
||||
#if CLIENT
|
||||
Light.Position += amount;
|
||||
|
||||
@@ -13,12 +13,13 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private float updateTimer;
|
||||
|
||||
[Flags]
|
||||
public enum TargetType
|
||||
{
|
||||
Any,
|
||||
Human,
|
||||
Monster,
|
||||
Wall
|
||||
Human = 1,
|
||||
Monster = 2,
|
||||
Wall = 4,
|
||||
Any = Human | Monster | Wall,
|
||||
}
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No, description: "Has the item currently detected movement. Intended to be used by StatusEffect conditionals (setting this value in XML has no effect).")]
|
||||
@@ -179,6 +180,11 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (!string.IsNullOrEmpty(signalOut)) { item.SendSignal(new Signal(signalOut, 1), "state_out"); }
|
||||
|
||||
if (MotionDetected)
|
||||
{
|
||||
ApplyStatusEffects(ActionType.OnUse, deltaTime);
|
||||
}
|
||||
|
||||
updateTimer -= deltaTime;
|
||||
if (updateTimer > 0.0f) { return; }
|
||||
|
||||
@@ -199,8 +205,7 @@ namespace Barotrauma.Items.Components
|
||||
float broadRangeX = Math.Max(rangeX * 2, 500);
|
||||
float broadRangeY = Math.Max(rangeY * 2, 500);
|
||||
|
||||
if (item.CurrentHull == null && item.Submarine != null &&
|
||||
(Target == TargetType.Wall || Target == TargetType.Any))
|
||||
if (item.CurrentHull == null && item.Submarine != null && Target.HasFlag(TargetType.Wall))
|
||||
{
|
||||
if (Level.Loaded != null && (Math.Abs(item.Submarine.Velocity.X) > MinimumVelocity || Math.Abs(item.Submarine.Velocity.Y) > MinimumVelocity))
|
||||
{
|
||||
@@ -248,7 +253,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
if (Target != TargetType.Wall)
|
||||
if (Target.HasFlag(TargetType.Human) || Target.HasFlag(TargetType.Monster))
|
||||
{
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
@@ -258,14 +263,13 @@ namespace Barotrauma.Items.Components
|
||||
//makes it possible to detect when a spawned character moves without triggering the detector immediately as the ragdoll spawns and drops to the ground
|
||||
if (c.SpawnTime > Timing.TotalTime - 1.0) { continue; }
|
||||
|
||||
switch (Target)
|
||||
if (c.IsHuman)
|
||||
{
|
||||
case TargetType.Human:
|
||||
if (!c.IsHuman) { continue; }
|
||||
break;
|
||||
case TargetType.Monster:
|
||||
if (c.IsHuman || c.IsPet) { continue; }
|
||||
break;
|
||||
if (!Target.HasFlag(TargetType.Human)) { continue; }
|
||||
}
|
||||
else if (!c.IsPet)
|
||||
{
|
||||
if (!Target.HasFlag(TargetType.Monster)) { continue; }
|
||||
}
|
||||
|
||||
//do a rough check based on the position of the character's collider first
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class OrComponent : AndComponent
|
||||
{
|
||||
public OrComponent(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
bool state = false;
|
||||
for (int i = 0; i < timeSinceReceived.Length; i++)
|
||||
{
|
||||
if (timeSinceReceived[i] <= timeFrame) { state = true; }
|
||||
timeSinceReceived[i] += deltaTime;
|
||||
}
|
||||
|
||||
string signalOut = state ? output : falseOutput;
|
||||
if (string.IsNullOrEmpty(signalOut))
|
||||
{
|
||||
//deactivate the component if state is false and there's no false output (will be woken up by non-zero signals in ReceiveSignal)
|
||||
if (!state) { IsActive = false; }
|
||||
return;
|
||||
}
|
||||
|
||||
item.SendSignal(new Signal(signalOut, sender: signalSender[0] ?? signalSender[1]), "signal_out");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,6 +64,11 @@ namespace Barotrauma.Items.Components
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public static int GetWaterPercentage(Hull hull)
|
||||
{
|
||||
return hull.WaterVolume > 1.0f ? MathHelper.Clamp((int)Math.Ceiling(hull.WaterPercentage), 0, 100) : 0;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (stateSwitchDelay > 0.0f)
|
||||
@@ -103,12 +108,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (item.CurrentHull != null)
|
||||
{
|
||||
int waterPercentage = 0;
|
||||
//ignore minuscule amounts of water
|
||||
if (item.CurrentHull.WaterVolume > 1.0f)
|
||||
{
|
||||
waterPercentage = MathHelper.Clamp((int)Math.Ceiling(item.CurrentHull.WaterPercentage), 0, 100);
|
||||
}
|
||||
int waterPercentage = GetWaterPercentage(item.CurrentHull);
|
||||
if (prevSentWaterPercentageValue != waterPercentage || waterPercentageSignal == null)
|
||||
{
|
||||
prevSentWaterPercentageValue = waterPercentage;
|
||||
|
||||
@@ -150,7 +150,7 @@ namespace Barotrauma.Items.Components
|
||||
// = no point in receiving
|
||||
if (!LinkToChat)
|
||||
{
|
||||
if (signalOutConnection == null || !signalOutConnection.Wires.Any(w => w != null))
|
||||
if (signalOutConnection == null || signalOutConnection.Wires.Count <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -143,12 +143,11 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (connections[i] == null || connections[i].Item != item) { continue; }
|
||||
|
||||
foreach (Wire wire in connections[i].Wires)
|
||||
if (connections[i].Wires.Contains(this))
|
||||
{
|
||||
if (wire != this) continue;
|
||||
SetConnectedDirty();
|
||||
|
||||
connections[i].SetWire(connections[i].FindWireIndex(wire), null);
|
||||
connections[i].DisconnectWire(this);
|
||||
}
|
||||
|
||||
connections[i] = null;
|
||||
@@ -597,15 +596,16 @@ namespace Barotrauma.Items.Components
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (connections[i] == null) { continue; }
|
||||
int wireIndex = connections[i].FindWireIndex(item);
|
||||
if (wireIndex == -1) { continue; }
|
||||
|
||||
var wire = connections[i].FindWireByItem(item);
|
||||
if (wire is null) { continue; }
|
||||
#if SERVER
|
||||
if (!connections[i].Item.Removed && (!connections[i].Item.Submarine?.Loading ?? true) && (!Level.Loaded?.Generating ?? true))
|
||||
{
|
||||
connections[i].Item.CreateServerEvent(connections[i].Item.GetComponent<ConnectionPanel>());
|
||||
}
|
||||
#endif
|
||||
connections[i].SetWire(wireIndex, null);
|
||||
connections[i].DisconnectWire(wire);
|
||||
connections[i] = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class XorComponent : AndComponent
|
||||
{
|
||||
public XorComponent(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
int receivedInputs = 0;
|
||||
for (int i = 0; i < timeSinceReceived.Length; i++)
|
||||
{
|
||||
if (timeSinceReceived[i] <= timeFrame) { receivedInputs += 1; }
|
||||
timeSinceReceived[i] += deltaTime;
|
||||
}
|
||||
|
||||
bool state = receivedInputs == 1;
|
||||
string signalOut = state ? output : falseOutput;
|
||||
if (string.IsNullOrEmpty(signalOut))
|
||||
{
|
||||
//deactivate the component if state is false and there's no false output (will be woken up by non-zero signals in ReceiveSignal)
|
||||
if (!state) { IsActive = false; }
|
||||
return;
|
||||
}
|
||||
|
||||
item.SendSignal(new Signal(signalOut, sender: signalSender[0] ?? signalSender[1]), "signal_out");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,9 @@ using FarseerPhysics.Dynamics;
|
||||
using FarseerPhysics.Dynamics.Contacts;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -13,6 +13,46 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
[Editable, Serialize(0.0f, IsPropertySaveable.Yes, description: "The maximum amount of force applied to the triggering entitites.", alwaysUseInstanceValues: true)]
|
||||
public float Force { get; set; }
|
||||
[Editable, Serialize(false, IsPropertySaveable.Yes, description: "Determines if the force gets higher the closer the triggerer is to the center of the trigger.", alwaysUseInstanceValues: true)]
|
||||
public bool DistanceBasedForce { get; set; }
|
||||
[Editable, Serialize(false, IsPropertySaveable.Yes, description: "Determines if the force fluctuates over time or if it stays constant.", alwaysUseInstanceValues: true)]
|
||||
public bool ForceFluctuation { get; set; }
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes, description: "How much the fluctuation affects the force. 1 is the maximum fluctuation, 0 is no fluctuation.", alwaysUseInstanceValues: true)]
|
||||
private float ForceFluctuationStrength
|
||||
{
|
||||
get
|
||||
{
|
||||
return forceFluctuationStrength;
|
||||
}
|
||||
set
|
||||
{
|
||||
forceFluctuationStrength = Math.Clamp(value, 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes, description: "How fast (cycles per second) the force fluctuates.", alwaysUseInstanceValues: true)]
|
||||
private float ForceFluctuationFrequency
|
||||
{
|
||||
get
|
||||
{
|
||||
return forceFluctuationFrequency;
|
||||
}
|
||||
set
|
||||
{
|
||||
forceFluctuationFrequency = Math.Max(value, 0.01f);
|
||||
}
|
||||
}
|
||||
[Serialize(0.01f, IsPropertySaveable.Yes, description: "How often (in seconds) the force fluctuation is calculated.", alwaysUseInstanceValues: true)]
|
||||
private float ForceFluctuationInterval
|
||||
{
|
||||
get
|
||||
{
|
||||
return forceFluctuationInterval;
|
||||
}
|
||||
set
|
||||
{
|
||||
forceFluctuationInterval = Math.Max(value, 0.01f);
|
||||
}
|
||||
}
|
||||
|
||||
public PhysicsBody PhysicsBody { get; private set; }
|
||||
private float Radius { get; set; }
|
||||
@@ -39,11 +79,6 @@ namespace Barotrauma.Items.Components
|
||||
private readonly LevelTrigger.TriggererType triggeredBy;
|
||||
private readonly HashSet<Entity> triggerers = new HashSet<Entity>();
|
||||
private readonly bool triggerOnce;
|
||||
private readonly bool distanceBasedForce;
|
||||
private readonly bool forceFluctuation;
|
||||
private readonly float forceFluctuationStrength;
|
||||
private readonly float forceFluctuationFrequency;
|
||||
private readonly float forceFluctuationInterval;
|
||||
private readonly List<ISerializableEntity> statusEffectTargets = new List<ISerializableEntity>();
|
||||
/// <summary>
|
||||
/// Effects applied to entities inside the trigger
|
||||
@@ -54,6 +89,10 @@ namespace Barotrauma.Items.Components
|
||||
/// </summary>
|
||||
private readonly List<Attack> attacks = new List<Attack>();
|
||||
|
||||
private float forceFluctuationStrength;
|
||||
private float forceFluctuationFrequency;
|
||||
private float forceFluctuationInterval;
|
||||
|
||||
public TriggerComponent(Item item, ContentXElement element) : base(item, element)
|
||||
{
|
||||
string triggeredByAttribute = element.GetAttributeString("triggeredby", "Character");
|
||||
@@ -62,15 +101,6 @@ namespace Barotrauma.Items.Components
|
||||
DebugConsole.ThrowError($"Error in ForceComponent config: \"{triggeredByAttribute}\" is not a valid triggerer type.");
|
||||
}
|
||||
triggerOnce = element.GetAttributeBool("triggeronce", false);
|
||||
distanceBasedForce = element.GetAttributeBool("distancebasedforce", false);
|
||||
forceFluctuation = element.GetAttributeBool("forcefluctuation", false);
|
||||
forceFluctuationStrength = element.GetAttributeFloat("forcefluctuationstrength", 1.0f);
|
||||
forceFluctuationStrength = Math.Clamp(forceFluctuationStrength, 0.0f, 1.0f);
|
||||
forceFluctuationFrequency = element.GetAttributeFloat("fluctuationfrequency", 1.0f);
|
||||
forceFluctuationFrequency = Math.Max(forceFluctuationFrequency, 0.01f);
|
||||
forceFluctuationInterval = element.GetAttributeFloat("fluctuationinterval", 0.01f);
|
||||
forceFluctuationInterval = Math.Max(forceFluctuationInterval, 0.01f);
|
||||
|
||||
string parentDebugName = $"TriggerComponent in {item.Name}";
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
@@ -93,13 +123,11 @@ namespace Barotrauma.Items.Components
|
||||
base.OnItemLoaded();
|
||||
float radiusAttribute = originalElement.GetAttributeFloat("radius", 10.0f);
|
||||
Radius = ConvertUnits.ToSimUnits(radiusAttribute * item.Scale);
|
||||
PhysicsBody = new PhysicsBody(0.0f, 0.0f, Radius, 1.5f)
|
||||
PhysicsBody = new PhysicsBody(0.0f, 0.0f, Radius, 1.5f, BodyType.Static, Physics.CollisionWall, LevelTrigger.GetCollisionCategories(triggeredBy))
|
||||
{
|
||||
BodyType = BodyType.Static,
|
||||
CollidesWith = LevelTrigger.GetCollisionCategories(triggeredBy),
|
||||
CollisionCategories = Physics.CollisionWall,
|
||||
UserData = item
|
||||
};
|
||||
PhysicsBody.SetTransformIgnoreContacts(item.SimPosition, 0.0f);
|
||||
PhysicsBody.FarseerBody.SetIsSensor(true);
|
||||
PhysicsBody.FarseerBody.OnCollision += OnCollision;
|
||||
PhysicsBody.FarseerBody.OnSeparation += OnSeparation;
|
||||
@@ -156,14 +184,14 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
TriggerActive = triggerers.Any();
|
||||
|
||||
if (forceFluctuation && TriggerActive && (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer))
|
||||
if (ForceFluctuation && TriggerActive && (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer))
|
||||
{
|
||||
ForceFluctuationTimer += deltaTime;
|
||||
if (ForceFluctuationTimer >= forceFluctuationInterval)
|
||||
if (ForceFluctuationTimer >= ForceFluctuationInterval)
|
||||
{
|
||||
float v = MathF.Sin(2 * MathF.PI * forceFluctuationFrequency * TimeInLevel);
|
||||
float v = MathF.Sin(2 * MathF.PI * ForceFluctuationFrequency * TimeInLevel);
|
||||
float amount = MathUtils.InverseLerp(-1.0f, 1.0f, v);
|
||||
CurrentForceFluctuation = MathHelper.Lerp(1.0f - forceFluctuationStrength, 1.0f, amount);
|
||||
CurrentForceFluctuation = MathHelper.Lerp(1.0f - ForceFluctuationStrength, 1.0f, amount);
|
||||
ForceFluctuationTimer = 0.0f;
|
||||
GameMain.NetworkMember?.CreateEntityEvent(this);
|
||||
}
|
||||
@@ -182,7 +210,7 @@ namespace Barotrauma.Items.Components
|
||||
LevelTrigger.ApplyAttacks(attacks, item.WorldPosition, deltaTime);
|
||||
}
|
||||
|
||||
if (Force < 0.01f)
|
||||
if (Math.Abs(Force) < 0.01f)
|
||||
{
|
||||
// Just ignore very minimal forces
|
||||
continue;
|
||||
@@ -208,21 +236,65 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
Vector2 diff = ConvertUnits.ToDisplayUnits(PhysicsBody.SimPosition - body.SimPosition);
|
||||
if (diff.LengthSquared() < 0.0001f) { return; }
|
||||
float distanceFactor = distanceBasedForce ? LevelTrigger.GetDistanceFactor(body, PhysicsBody, RadiusInDisplayUnits) : 1.0f;
|
||||
float distanceFactor = DistanceBasedForce ? LevelTrigger.GetDistanceFactor(body, PhysicsBody, RadiusInDisplayUnits) : 1.0f;
|
||||
if (distanceFactor <= 0.0f) { return; }
|
||||
Vector2 force = distanceFactor * (CurrentForceFluctuation * Force) * Vector2.Normalize(diff);
|
||||
if (force.LengthSquared() < 0.01f) { return; }
|
||||
body.ApplyForce(force);
|
||||
}
|
||||
|
||||
public override void Move(Vector2 amount)
|
||||
public override void Move(Vector2 amount, bool ignoreContacts = false)
|
||||
{
|
||||
base.Move(amount);
|
||||
if (PhysicsBody != null)
|
||||
{
|
||||
PhysicsBody.SetTransform(PhysicsBody.SimPosition + ConvertUnits.ToSimUnits(amount), 0.0f);
|
||||
if (ignoreContacts)
|
||||
{
|
||||
PhysicsBody.SetTransformIgnoreContacts(PhysicsBody.SimPosition + ConvertUnits.ToSimUnits(amount), 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
PhysicsBody.SetTransform(PhysicsBody.SimPosition + ConvertUnits.ToSimUnits(amount), 0.0f);
|
||||
}
|
||||
PhysicsBody.Submarine = item.Submarine;
|
||||
}
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(Signal signal, Connection connection)
|
||||
{
|
||||
base.ReceiveSignal(signal, connection);
|
||||
switch (connection.Name)
|
||||
{
|
||||
case "set_force":
|
||||
if (!FloatTryParse(signal, out float force)) { break; }
|
||||
Force = force;
|
||||
break;
|
||||
case "set_distancebasedforce":
|
||||
if (!bool.TryParse(signal.value, out bool distanceBasedForce)) { break; }
|
||||
DistanceBasedForce = distanceBasedForce;
|
||||
break;
|
||||
case "set_forcefluctuation":
|
||||
if (!bool.TryParse(signal.value, out bool forceFluctuation)) { break; }
|
||||
ForceFluctuation = forceFluctuation;
|
||||
break;
|
||||
case "set_forcefluctuationstrength":
|
||||
if (!FloatTryParse(signal, out float forceFluctuationStrength)) { break; }
|
||||
ForceFluctuationStrength = forceFluctuationStrength;
|
||||
break;
|
||||
case "set_forcefluctuationfrequency":
|
||||
if (!FloatTryParse(signal, out float forceFluctuationFrequency)) { break; }
|
||||
ForceFluctuationFrequency = forceFluctuationFrequency;
|
||||
break;
|
||||
case "set_forcefluctuationinterval":
|
||||
if (!FloatTryParse(signal, out float forceFluctuationInterval)) { break; }
|
||||
ForceFluctuationInterval = forceFluctuationInterval;
|
||||
break;
|
||||
}
|
||||
|
||||
static bool FloatTryParse(Signal signal, out float value)
|
||||
{
|
||||
return float.TryParse(signal.value, NumberStyles.Any, CultureInfo.InvariantCulture, out value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,7 @@ using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using Barotrauma.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using FarseerPhysics.Dynamics;
|
||||
|
||||
@@ -20,8 +18,6 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private Vector2 barrelPos;
|
||||
private Vector2 transformedBarrelPos;
|
||||
|
||||
private LightComponent lightComponent;
|
||||
|
||||
private float rotation, targetRotation;
|
||||
|
||||
@@ -71,6 +67,8 @@ namespace Barotrauma.Items.Components
|
||||
public Character ActiveUser;
|
||||
private float resetActiveUserTimer;
|
||||
|
||||
private List<LightComponent> lightComponents;
|
||||
|
||||
public float Rotation
|
||||
{
|
||||
get { return rotation; }
|
||||
@@ -168,10 +166,13 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
rotation = (minRotation + maxRotation) / 2;
|
||||
#if CLIENT
|
||||
if (lightComponent != null)
|
||||
if (lightComponents != null)
|
||||
{
|
||||
lightComponent.Rotation = rotation;
|
||||
lightComponent.Light.Rotation = -rotation;
|
||||
foreach (var light in lightComponents)
|
||||
{
|
||||
light.Rotation = rotation;
|
||||
light.Light.Rotation = -rotation;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -331,27 +332,39 @@ namespace Barotrauma.Items.Components
|
||||
if (loadedRotationLimits.HasValue) { RotationLimits = loadedRotationLimits.Value; }
|
||||
if (loadedBaseRotation.HasValue) { BaseRotation = loadedBaseRotation.Value; }
|
||||
targetRotation = rotation;
|
||||
FindLightComponent();
|
||||
UpdateTransformedBarrelPos();
|
||||
}
|
||||
|
||||
private void FindLightComponent()
|
||||
private void FindLightComponents()
|
||||
{
|
||||
if (lightComponents != null)
|
||||
{
|
||||
// Can't run again, because of reparenting.
|
||||
return;
|
||||
}
|
||||
foreach (LightComponent lc in item.GetComponents<LightComponent>())
|
||||
{
|
||||
// Only make the Turret control the LightComponents that are it's children. So it'd be possible to for example have some extra lights on the turret that don't rotate with it.
|
||||
if (lc?.Parent == this)
|
||||
{
|
||||
lightComponent = lc;
|
||||
break;
|
||||
if (lightComponents == null)
|
||||
{
|
||||
lightComponents = new List<LightComponent>();
|
||||
}
|
||||
lightComponents.Add(lc);
|
||||
}
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
if (lightComponent != null)
|
||||
if (lightComponents != null)
|
||||
{
|
||||
lightComponent.Parent = null;
|
||||
lightComponent.Rotation = Rotation - MathHelper.ToRadians(item.Rotation);
|
||||
lightComponent.Light.Rotation = -rotation;
|
||||
foreach (var light in lightComponents)
|
||||
{
|
||||
// We want the turret to control the state of the LightComponent, not tie it's state to the state of the Turret (the light can be inactive even if the turret is active)
|
||||
light.Parent = null;
|
||||
light.Rotation = Rotation - item.RotationRad;
|
||||
light.Light.Rotation = -rotation;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -428,7 +441,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (MathUtils.NearlyEqual(minRotation, maxRotation))
|
||||
{
|
||||
UpdateLightComponent();
|
||||
UpdateLightComponents();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -452,7 +465,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
// Do not increase the weapons skill when operating a turret in an outpost level
|
||||
if (user?.Info != null && (GameMain.GameSession?.Campaign == null || !Level.IsLoadedOutpost))
|
||||
if (user?.Info != null && (GameMain.GameSession?.Campaign == null || !Level.IsLoadedFriendlyOutpost))
|
||||
{
|
||||
user.Info.IncreaseSkillLevel("weapons".ToIdentifier(),
|
||||
SkillSettings.Current.SkillIncreasePerSecondWhenOperatingTurret * deltaTime / Math.Max(user.GetSkillLevel("weapons"), 1.0f));
|
||||
@@ -509,14 +522,17 @@ namespace Barotrauma.Items.Components
|
||||
aiFindTargetTimer -= deltaTime;
|
||||
}
|
||||
|
||||
UpdateLightComponent();
|
||||
UpdateLightComponents();
|
||||
}
|
||||
|
||||
private void UpdateLightComponent()
|
||||
private void UpdateLightComponents()
|
||||
{
|
||||
if (lightComponent != null)
|
||||
if (lightComponents != null)
|
||||
{
|
||||
lightComponent.Rotation = Rotation - MathHelper.ToRadians(item.Rotation);
|
||||
foreach (var light in lightComponents)
|
||||
{
|
||||
light.Rotation = Rotation - item.RotationRad;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -661,6 +677,7 @@ namespace Barotrauma.Items.Components
|
||||
while (neededPower > 0.0001f && batteries.Count > 0)
|
||||
{
|
||||
batteries.RemoveAll(b => b.Charge <= 0.0001f || b.MaxOutPut <= 0.0001f);
|
||||
if (!batteries.Any()) { break; }
|
||||
float takePower = neededPower / batteries.Count;
|
||||
takePower = Math.Min(takePower, batteries.Min(b => Math.Min(b.Charge * 3600.0f, b.MaxOutPut)));
|
||||
foreach (PowerContainer battery in batteries)
|
||||
@@ -1151,8 +1168,12 @@ namespace Barotrauma.Items.Components
|
||||
foreach (Character enemy in Character.CharacterList)
|
||||
{
|
||||
// Ignore dead, friendly, and those that are inside the same sub
|
||||
if (enemy.IsDead || !enemy.Enabled || enemy.Submarine == character.Submarine) { continue; }
|
||||
if (enemy.Submarine != null && enemy.Submarine.TeamID == character.Submarine.TeamID) { continue; }
|
||||
if (enemy.IsDead || !enemy.Enabled) { continue; }
|
||||
if (character.Submarine != null)
|
||||
{
|
||||
if (enemy.Submarine == character.Submarine) { continue; }
|
||||
if (enemy.Submarine != null && enemy.Submarine.TeamID == character.Submarine.TeamID) { continue; }
|
||||
}
|
||||
// Don't aim monsters that are inside any submarine.
|
||||
if (!enemy.IsHuman && enemy.CurrentHull != null) { continue; }
|
||||
if (HumanAIController.IsFriendly(character, enemy)) { continue; }
|
||||
@@ -1596,21 +1617,24 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
break;
|
||||
case "toggle_light":
|
||||
if (lightComponent != null && signal.value != "0")
|
||||
if (lightComponents != null && signal.value != "0")
|
||||
{
|
||||
lightComponent.IsOn = !lightComponent.IsOn;
|
||||
UpdateLightComponent();
|
||||
foreach (var light in lightComponents)
|
||||
{
|
||||
light.IsOn = !light.IsOn;
|
||||
}
|
||||
UpdateLightComponents();
|
||||
}
|
||||
break;
|
||||
case "set_light":
|
||||
if (lightComponent != null)
|
||||
if (lightComponents != null)
|
||||
{
|
||||
bool shouldBeOn = signal.value != "0";
|
||||
if (shouldBeOn != lightComponent.IsOn)
|
||||
foreach (var light in lightComponents)
|
||||
{
|
||||
lightComponent.IsOn = shouldBeOn;
|
||||
UpdateLightComponent();
|
||||
light.IsOn = shouldBeOn;
|
||||
}
|
||||
UpdateLightComponents();
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1628,7 +1652,7 @@ namespace Barotrauma.Items.Components
|
||||
public override void OnItemLoaded()
|
||||
{
|
||||
base.OnItemLoaded();
|
||||
FindLightComponent();
|
||||
FindLightComponents();
|
||||
targetRotation = rotation;
|
||||
if (!loadedBaseRotation.HasValue)
|
||||
{
|
||||
|
||||
@@ -6,7 +6,6 @@ using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Networking;
|
||||
using System.Collections.Immutable;
|
||||
using Barotrauma.Abilities;
|
||||
|
||||
namespace Barotrauma
|
||||
@@ -18,7 +17,6 @@ namespace Barotrauma
|
||||
Beard,
|
||||
Moustache,
|
||||
FaceAttachment,
|
||||
JobIndicator,
|
||||
Husk,
|
||||
Herpes
|
||||
}
|
||||
@@ -82,7 +80,20 @@ namespace Barotrauma
|
||||
public string Sound { get; private set; }
|
||||
public Point? SheetIndex { get; private set; }
|
||||
|
||||
public LightComponent LightComponent { get; set; }
|
||||
public LightComponent LightComponent => LightComponents?.FirstOrDefault();
|
||||
|
||||
public List<LightComponent> LightComponents
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_lightComponents == null)
|
||||
{
|
||||
_lightComponents = new List<LightComponent>();
|
||||
}
|
||||
return _lightComponents;
|
||||
}
|
||||
}
|
||||
private List<LightComponent> _lightComponents;
|
||||
|
||||
public int Variant { get; set; }
|
||||
|
||||
@@ -116,7 +127,6 @@ namespace Barotrauma
|
||||
case WearableType.Beard:
|
||||
case WearableType.Moustache:
|
||||
case WearableType.FaceAttachment:
|
||||
case WearableType.JobIndicator:
|
||||
case WearableType.Husk:
|
||||
case WearableType.Herpes:
|
||||
Limb = LimbType.Head;
|
||||
@@ -338,11 +348,14 @@ namespace Barotrauma.Items.Components
|
||||
foreach (var lightElement in subElement.Elements())
|
||||
{
|
||||
if (!lightElement.Name.ToString().Equals("lightcomponent", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
wearableSprites[i].LightComponent = new LightComponent(item, lightElement)
|
||||
wearableSprites[i].LightComponents.Add(new LightComponent(item, lightElement)
|
||||
{
|
||||
Parent = this
|
||||
};
|
||||
item.AddComponent(wearableSprites[i].LightComponent);
|
||||
});
|
||||
foreach (var light in wearableSprites[i].LightComponents)
|
||||
{
|
||||
item.AddComponent(light);
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
@@ -413,7 +426,10 @@ namespace Barotrauma.Items.Components
|
||||
IsActive = true;
|
||||
if (wearableSprite.LightComponent != null)
|
||||
{
|
||||
wearableSprite.LightComponent.ParentBody = equipLimb.body;
|
||||
foreach (var light in wearableSprite.LightComponents)
|
||||
{
|
||||
light.ParentBody = equipLimb.body;
|
||||
}
|
||||
}
|
||||
|
||||
limb[i] = equipLimb;
|
||||
@@ -467,7 +483,10 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (wearableSprites[i].LightComponent != null)
|
||||
{
|
||||
wearableSprites[i].LightComponent.ParentBody = null;
|
||||
foreach (var light in wearableSprites[i].LightComponents)
|
||||
{
|
||||
light.ParentBody = null;
|
||||
}
|
||||
}
|
||||
|
||||
equipLimb.WearingItems.RemoveAll(w => w != null && w == wearableSprites[i]);
|
||||
@@ -494,7 +513,6 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
item.SetTransform(picker.SimPosition, 0.0f);
|
||||
item.SetContainedItemPositions();
|
||||
|
||||
item.ApplyStatusEffects(ActionType.OnWearing, deltaTime, picker);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user