(6eeea9b7c) v0.9.10.0.0
This commit is contained in:
@@ -5,7 +5,7 @@ using FarseerPhysics.Dynamics.Joints;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Barotrauma.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
@@ -29,6 +29,7 @@ namespace Barotrauma.Items.Components
|
||||
private Door door;
|
||||
|
||||
private Body[] bodies;
|
||||
private Fixture outsideBlocker;
|
||||
private Body doorBody;
|
||||
|
||||
private bool docked;
|
||||
@@ -58,7 +59,7 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "If set to true, this docking port is used when spawning the submarine docked to an outpost (if possible).")]
|
||||
[Editable, Serialize(false, true, description: "If set to true, this docking port is used when spawning the submarine docked to an outpost (if possible).")]
|
||||
public bool MainDockingPort
|
||||
{
|
||||
get;
|
||||
@@ -113,6 +114,12 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (DockingTarget != null)
|
||||
{
|
||||
if (IsHorizontal)
|
||||
{
|
||||
DockingDir = 0;
|
||||
DockingDir = GetDir(DockingTarget);
|
||||
DockingTarget.DockingDir = -DockingDir;
|
||||
}
|
||||
if (joint != null)
|
||||
{
|
||||
CreateJoint(joint is WeldJoint);
|
||||
@@ -198,18 +205,6 @@ namespace Barotrauma.Items.Components
|
||||
DockingDir = GetDir(DockingTarget);
|
||||
DockingTarget.DockingDir = -DockingDir;
|
||||
|
||||
if (door != null && DockingTarget.door != null)
|
||||
{
|
||||
WayPoint myWayPoint = WayPoint.WayPointList.Find(wp => door.LinkedGap == wp.ConnectedGap);
|
||||
WayPoint targetWayPoint = WayPoint.WayPointList.Find(wp => DockingTarget.door.LinkedGap == wp.ConnectedGap);
|
||||
|
||||
if (myWayPoint != null && targetWayPoint != null)
|
||||
{
|
||||
myWayPoint.linkedTo.Add(targetWayPoint);
|
||||
targetWayPoint.linkedTo.Add(myWayPoint);
|
||||
}
|
||||
}
|
||||
|
||||
CreateJoint(false);
|
||||
|
||||
#if SERVER
|
||||
@@ -259,6 +254,12 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
item.CreateServerEvent(this);
|
||||
}
|
||||
#else
|
||||
if (GameMain.Client != null && GameMain.Client.MidRoundSyncing &&
|
||||
(item.Submarine == Submarine.MainSub || DockingTarget.item.Submarine == Submarine.MainSub))
|
||||
{
|
||||
Screen.Selected.Cam.Position = Submarine.MainSub.WorldPosition;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -270,6 +271,20 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
CreateHulls();
|
||||
}
|
||||
|
||||
if (door != null && DockingTarget.door != null)
|
||||
{
|
||||
WayPoint myWayPoint = WayPoint.WayPointList.Find(wp => door.LinkedGap == wp.ConnectedGap);
|
||||
WayPoint targetWayPoint = WayPoint.WayPointList.Find(wp => DockingTarget.door.LinkedGap == wp.ConnectedGap);
|
||||
|
||||
if (myWayPoint != null && targetWayPoint != null)
|
||||
{
|
||||
myWayPoint.FindHull();
|
||||
myWayPoint.linkedTo.Add(targetWayPoint);
|
||||
targetWayPoint.FindHull();
|
||||
targetWayPoint.linkedTo.Add(myWayPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -461,6 +476,12 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
if (leftSubRightSide == int.MinValue || rightSubLeftSide == int.MaxValue)
|
||||
{
|
||||
DebugConsole.NewMessage("Creating hulls between docking ports failed. Could not find a hull next to the docking port.");
|
||||
return;
|
||||
}
|
||||
|
||||
//expand left hull to the rightmost hull of the sub at the left side
|
||||
//(unless the difference is more than 100 units - if the distance is very large
|
||||
//there's something wrong with the positioning of the docking ports or submarine hulls)
|
||||
@@ -469,7 +490,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (leftHullDiff > 100)
|
||||
{
|
||||
DebugConsole.ThrowError("Creating hulls between docking ports failed. The leftmost docking port seems to be very far from any hulls in the left-side submarine.");
|
||||
DebugConsole.NewMessage("Creating hulls between docking ports failed. The leftmost docking port seems to be very far from any hulls in the left-side submarine.");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -483,7 +505,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (rightHullDiff > 100)
|
||||
{
|
||||
DebugConsole.ThrowError("Creating hulls between docking ports failed. The rightmost docking port seems to be very far from any hulls in the right-side submarine.");
|
||||
DebugConsole.NewMessage("Creating hulls between docking ports failed. The rightmost docking port seems to be very far from any hulls in the right-side submarine.");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -506,6 +529,16 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
if (rightHullDiff <= 100 && hulls[0].Submarine != null)
|
||||
{
|
||||
outsideBlocker = hulls[0].Submarine.PhysicsBody.FarseerBody.CreateRectangle(
|
||||
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));
|
||||
outsideBlocker.UserData = this;
|
||||
}
|
||||
|
||||
gap = new Gap(new Rectangle(hullRects[0].Right - 2, hullRects[0].Y, 4, hullRects[0].Height), true, subs[0]);
|
||||
}
|
||||
else
|
||||
@@ -540,6 +573,12 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
if (upperSubBottom == int.MaxValue || lowerSubTop == int.MinValue)
|
||||
{
|
||||
DebugConsole.NewMessage("Creating hulls between docking ports failed. Could not find a hull next to the docking port.");
|
||||
return;
|
||||
}
|
||||
|
||||
//expand lower hull to the topmost hull of the lower sub
|
||||
//(unless the difference is more than 100 units - if the distance is very large
|
||||
//there's something wrong with the positioning of the docking ports or submarine hulls)
|
||||
@@ -548,7 +587,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (lowerHullDiff > 100)
|
||||
{
|
||||
DebugConsole.ThrowError("Creating hulls between docking ports failed. The lower docking port seems to be very far from any hulls in the lower submarine.");
|
||||
DebugConsole.NewMessage("Creating hulls between docking ports failed. The lower docking port seems to be very far from any hulls in the lower submarine.");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -561,7 +601,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (upperHullDiff > 100)
|
||||
{
|
||||
DebugConsole.ThrowError("Creating hulls between docking ports failed. The upper docking port seems to be very far from any hulls in the upper submarine.");
|
||||
DebugConsole.NewMessage("Creating hulls between docking ports failed. The upper docking port seems to be very far from any hulls in the upper submarine.");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -575,7 +616,8 @@ namespace Barotrauma.Items.Components
|
||||
int midHullDiff = ((hullRects[1].Y - hullRects[1].Height) - hullRects[0].Y) + 2;
|
||||
if (midHullDiff > 100)
|
||||
{
|
||||
DebugConsole.ThrowError("Creating hulls between docking ports failed. The upper hull seems to be very far from the lower hull.");
|
||||
DebugConsole.NewMessage("Creating hulls between docking ports failed. The upper hull seems to be very far from the lower hull.");
|
||||
return;
|
||||
}
|
||||
else if (midHullDiff > 0)
|
||||
{
|
||||
@@ -584,15 +626,33 @@ namespace Barotrauma.Items.Components
|
||||
hullRects[1].Height += midHullDiff / 2 + 1;
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
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].AddToGrid(subs[i]);
|
||||
hulls[i].FreeID();
|
||||
|
||||
for (int j = 0; j < 2; j++)
|
||||
{
|
||||
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)));
|
||||
}
|
||||
}
|
||||
|
||||
gap = new Gap(new Rectangle(hullRects[0].X, hullRects[0].Y+2, hullRects[0].Width, 4), false, subs[0]);
|
||||
if (midHullDiff <= 100 && hulls[0].Submarine != null)
|
||||
{
|
||||
outsideBlocker = hulls[0].Submarine.PhysicsBody.FarseerBody.CreateRectangle(
|
||||
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));
|
||||
outsideBlocker.UserData = this;
|
||||
}
|
||||
|
||||
gap = new Gap(new Rectangle(hullRects[0].X, hullRects[0].Y + 2, hullRects[0].Width, 4), false, subs[0]);
|
||||
}
|
||||
|
||||
LinkHullsToGaps();
|
||||
@@ -609,7 +669,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
foreach (Body body in bodies)
|
||||
{
|
||||
if (body == null) continue;
|
||||
if (body == null) { continue; }
|
||||
body.BodyType = BodyType.Static;
|
||||
body.Friction = 0.5f;
|
||||
|
||||
@@ -720,7 +780,9 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (myWayPoint != null && targetWayPoint != null)
|
||||
{
|
||||
myWayPoint.FindHull();
|
||||
myWayPoint.linkedTo.Remove(targetWayPoint);
|
||||
targetWayPoint.FindHull();
|
||||
targetWayPoint.linkedTo.Remove(myWayPoint);
|
||||
}
|
||||
}
|
||||
@@ -769,6 +831,9 @@ namespace Barotrauma.Items.Components
|
||||
bodies = null;
|
||||
}
|
||||
|
||||
outsideBlocker?.Body.Remove(outsideBlocker);
|
||||
outsideBlocker = null;
|
||||
|
||||
Item.Submarine.EnableObstructedWaypoints();
|
||||
obstructedWayPointsDisabled = false;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ using FarseerPhysics.Dynamics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Barotrauma.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
#if CLIENT
|
||||
@@ -38,12 +38,17 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
//how much "less stuck" partially doors get when opened
|
||||
const float StuckReductionOnOpen = 30.0f;
|
||||
|
||||
private float resetPredictionTimer;
|
||||
private float toggleCooldownTimer;
|
||||
private Character lastUser;
|
||||
|
||||
private float damageSoundCooldown;
|
||||
|
||||
private double lastBrokenTime;
|
||||
|
||||
private Rectangle doorRect;
|
||||
|
||||
private bool isBroken;
|
||||
@@ -53,7 +58,7 @@ namespace Barotrauma.Items.Components
|
||||
get { return isBroken; }
|
||||
set
|
||||
{
|
||||
if (isBroken == value) return;
|
||||
if (isBroken == value) { return; }
|
||||
isBroken = value;
|
||||
if (isBroken)
|
||||
{
|
||||
@@ -63,6 +68,9 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
EnableBody();
|
||||
}
|
||||
#if SERVER
|
||||
item.CreateServerEvent(this);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +93,7 @@ namespace Barotrauma.Items.Components
|
||||
if (isOpen || isBroken || !CanBeWelded) return;
|
||||
stuck = MathHelper.Clamp(value, 0.0f, 100.0f);
|
||||
if (stuck <= 0.0f) { IsStuck = false; }
|
||||
if (stuck >= 100.0f) { IsStuck = true; }
|
||||
if (stuck >= 99.0f) { IsStuck = true; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,10 +211,16 @@ namespace Barotrauma.Items.Components
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public override void OnItemLoaded()
|
||||
{
|
||||
//do this here because the scale of the item might not be set to the final value yet in the constructor
|
||||
doorRect = new Rectangle(
|
||||
item.Rect.Center.X - (int)(doorSprite.size.X / 2 * item.Scale),
|
||||
item.Rect.Y - item.Rect.Height/2 + (int)(doorSprite.size.Y / 2.0f * item.Scale),
|
||||
item.Rect.Y - item.Rect.Height / 2 + (int)(doorSprite.size.Y / 2.0f * item.Scale),
|
||||
(int)(doorSprite.size.X * item.Scale),
|
||||
(int)(doorSprite.size.Y * item.Scale));
|
||||
|
||||
@@ -224,8 +238,6 @@ namespace Barotrauma.Items.Components
|
||||
Body.SetTransformIgnoreContacts(
|
||||
ConvertUnits.ToSimUnits(new Vector2(doorRect.Center.X, doorRect.Y - doorRect.Height / 2)),
|
||||
0.0f);
|
||||
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public override void Move(Vector2 amount)
|
||||
@@ -295,6 +307,7 @@ namespace Barotrauma.Items.Components
|
||||
PickingTime = 0;
|
||||
ToggleState(ActionType.OnUse, character);
|
||||
PickingTime = originalPickingTime;
|
||||
StopPicking(picker);
|
||||
}
|
||||
#if CLIENT
|
||||
else if (hasRequiredItems && character != null && character == Character.Controlled)
|
||||
@@ -313,8 +326,9 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (isBroken)
|
||||
{
|
||||
lastBrokenTime = Timing.TotalTime;
|
||||
//the door has to be restored to 50% health before collision detection on the body is re-enabled
|
||||
if (item.ConditionPercentage > 50.0f)
|
||||
if (item.ConditionPercentage > 50.0f && (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer))
|
||||
{
|
||||
IsBroken = false;
|
||||
}
|
||||
@@ -363,7 +377,10 @@ namespace Barotrauma.Items.Components
|
||||
public override void UpdateBroken(float deltaTime, Camera cam)
|
||||
{
|
||||
base.UpdateBroken(deltaTime, cam);
|
||||
IsBroken = true;
|
||||
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
IsBroken = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void EnableBody()
|
||||
@@ -502,6 +519,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
foreach (Limb limb in c.AnimController.Limbs)
|
||||
{
|
||||
if (limb.IsSevered) { continue; }
|
||||
if (PushBodyOutOfDoorway(c, limb.body, dir, simPos, simSize) && damageSoundCooldown <= 0.0f)
|
||||
{
|
||||
#if CLIENT
|
||||
@@ -564,7 +582,12 @@ namespace Barotrauma.Items.Components
|
||||
body.ApplyLinearImpulse(new Vector2(dir * 2.0f, isOpen ? 0.0f : -1.0f), maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
|
||||
}
|
||||
|
||||
c.SetStun(0.2f);
|
||||
//don't stun if the door was broken a moment ago
|
||||
//otherwise enabling the door's collider and pushing the character away will interrupt repairing
|
||||
if (lastBrokenTime < Timing.TotalTime - 1.0f)
|
||||
{
|
||||
c.SetStun(0.2f);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class ElectricalDischarger : Powered
|
||||
partial class ElectricalDischarger : Powered, IServerSerializable
|
||||
{
|
||||
private static readonly List<ElectricalDischarger> list = new List<ElectricalDischarger>();
|
||||
public static IEnumerable<ElectricalDischarger> List
|
||||
@@ -48,14 +48,14 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(500.0f, true, description: "How far the discharge can travel from the item."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 5000.0f)]
|
||||
[Serialize(500.0f, true, description: "How far the discharge can travel from the item.", alwaysUseInstanceValues: true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 5000.0f)]
|
||||
public float Range
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(25.0f, true, description: "How much further can the discharge be carried when moving across walls."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f)]
|
||||
[Serialize(25.0f, true, description: "How much further can the discharge be carried when moving across walls.", alwaysUseInstanceValues: true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f)]
|
||||
public float RangeMultiplierInWalls
|
||||
{
|
||||
get;
|
||||
@@ -115,10 +115,15 @@ namespace Barotrauma.Items.Components
|
||||
//already active, do nothing
|
||||
if (IsActive) { return false; }
|
||||
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return false; }
|
||||
|
||||
CurrPowerConsumption = powerConsumption;
|
||||
charging = true;
|
||||
timer = Duration;
|
||||
IsActive = true;
|
||||
#if SERVER
|
||||
if (GameMain.Server != null) { item.CreateServerEvent(this); }
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -150,14 +155,12 @@ namespace Barotrauma.Items.Components
|
||||
neededPower -= takePower;
|
||||
battery.Charge -= takePower / 3600.0f;
|
||||
#if SERVER
|
||||
if (GameMain.Server != null)
|
||||
{
|
||||
battery.Item.CreateServerEvent(battery);
|
||||
}
|
||||
if (GameMain.Server != null) { battery.Item.CreateServerEvent(battery); }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
Discharge();
|
||||
|
||||
}
|
||||
else if (Voltage > MinVoltage)
|
||||
{
|
||||
@@ -478,5 +481,10 @@ namespace Barotrauma.Items.Components
|
||||
base.RemoveComponentSpecific();
|
||||
list.Remove(this);
|
||||
}
|
||||
|
||||
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
|
||||
{
|
||||
//no further data needed, the event just triggers the discharge
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using FarseerPhysics;
|
||||
using FarseerPhysics.Dynamics;
|
||||
using FarseerPhysics.Dynamics.Contacts;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
|
||||
@@ -309,8 +310,13 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
picker = character;
|
||||
|
||||
if (character != null) item.Submarine = character.Submarine;
|
||||
if (item.Removed)
|
||||
{
|
||||
DebugConsole.ThrowError($"Attempted to equip a removed item ({item.Name})\n" + Environment.StackTrace);
|
||||
return;
|
||||
}
|
||||
|
||||
if (character != null) { item.Submarine = character.Submarine; }
|
||||
if (item.body == null)
|
||||
{
|
||||
if (body != null)
|
||||
@@ -325,8 +331,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (!item.body.Enabled)
|
||||
{
|
||||
Limb rightHand = picker.AnimController.GetLimb(LimbType.RightHand);
|
||||
item.SetTransform(rightHand.SimPosition, 0.0f);
|
||||
Limb hand = picker.AnimController.GetLimb(LimbType.RightHand) ?? picker.AnimController.GetLimb(LimbType.LeftHand);
|
||||
item.SetTransform(hand != null ? hand.SimPosition : character.SimPosition, 0.0f);
|
||||
}
|
||||
|
||||
bool alreadyEquipped = character.HasEquippedItem(item);
|
||||
@@ -363,38 +369,61 @@ namespace Barotrauma.Items.Components
|
||||
IsActive = false;
|
||||
}
|
||||
|
||||
public bool CanBeAttached()
|
||||
public bool CanBeAttached(Character user)
|
||||
{
|
||||
if (!attachable || !Reattachable) return false;
|
||||
if (!attachable || !Reattachable) { return false; }
|
||||
|
||||
//can be attached anywhere in sub editor
|
||||
if (Screen.Selected == GameMain.SubEditorScreen) return true;
|
||||
if (Screen.Selected == GameMain.SubEditorScreen) { return true; }
|
||||
|
||||
Vector2 attachPos = user == null ? item.WorldPosition : GetAttachPosition(user, useWorldCoordinates: true);
|
||||
|
||||
//can be attached anywhere inside hulls
|
||||
if (item.CurrentHull != null) return true;
|
||||
if (item.CurrentHull != null && Submarine.RectContains(item.CurrentHull.WorldRect, attachPos)) { return true; }
|
||||
|
||||
return Structure.GetAttachTarget(item.WorldPosition) != null;
|
||||
return Structure.GetAttachTarget(attachPos) != null;
|
||||
}
|
||||
|
||||
public bool CanBeDeattached()
|
||||
{
|
||||
if (!attachable || !attached) return true;
|
||||
if (!attachable || !attached) { return true; }
|
||||
|
||||
//allow deattaching everywhere in sub editor
|
||||
if (Screen.Selected == GameMain.SubEditorScreen) return true;
|
||||
if (Screen.Selected == GameMain.SubEditorScreen) { return true; }
|
||||
|
||||
//don't allow deattaching if part of a sub and outside hulls
|
||||
return item.Submarine == null || item.CurrentHull != null;
|
||||
if (item.GetComponent<LevelResource>() != null) { return true; }
|
||||
|
||||
//if the item has a connection panel and rewiring is disabled, don't allow deattaching
|
||||
var connectionPanel = item.GetComponent<ConnectionPanel>();
|
||||
if (connectionPanel != null && (connectionPanel.Locked || !(GameMain.NetworkMember?.ServerSettings?.AllowRewiring ?? true)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (item.CurrentHull == null)
|
||||
{
|
||||
return Structure.GetAttachTarget(item.WorldPosition) != null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Pick(Character picker)
|
||||
{
|
||||
if (item.Removed)
|
||||
{
|
||||
DebugConsole.ThrowError($"Attempted to pick up a removed item ({item.Name})\n" + Environment.StackTrace);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!attachable)
|
||||
{
|
||||
return base.Pick(picker);
|
||||
}
|
||||
|
||||
if (!CanBeDeattached()) return false;
|
||||
if (!CanBeDeattached()) { return false; }
|
||||
|
||||
if (Attached)
|
||||
{
|
||||
@@ -486,7 +515,7 @@ namespace Barotrauma.Items.Components
|
||||
if (character != null)
|
||||
{
|
||||
if (!character.IsKeyDown(InputType.Aim)) { return false; }
|
||||
if (!CanBeAttached()) { return false; }
|
||||
if (!CanBeAttached(character)) { return false; }
|
||||
|
||||
if (GameMain.NetworkMember != null)
|
||||
{
|
||||
@@ -515,7 +544,7 @@ namespace Barotrauma.Items.Components
|
||||
else
|
||||
{
|
||||
item.Drop(character);
|
||||
item.SetTransform(ConvertUnits.ToSimUnits(GetAttachPosition(character)), 0.0f);
|
||||
item.SetTransform(ConvertUnits.ToSimUnits(GetAttachPosition(character)), 0.0f, findNewHull: false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -524,16 +553,18 @@ namespace Barotrauma.Items.Components
|
||||
return true;
|
||||
}
|
||||
|
||||
private Vector2 GetAttachPosition(Character user)
|
||||
private Vector2 GetAttachPosition(Character user, bool useWorldCoordinates = false)
|
||||
{
|
||||
if (user == null) { return item.Position; }
|
||||
if (user == null) { return useWorldCoordinates ? item.WorldPosition : item.Position; }
|
||||
|
||||
Vector2 mouseDiff = user.CursorWorldPosition - user.WorldPosition;
|
||||
mouseDiff = mouseDiff.ClampLength(MaxAttachDistance);
|
||||
|
||||
Vector2 userPos = useWorldCoordinates ? user.WorldPosition : user.Position;
|
||||
|
||||
return new Vector2(
|
||||
MathUtils.RoundTowardsClosest(user.Position.X + mouseDiff.X, Submarine.GridSize.X),
|
||||
MathUtils.RoundTowardsClosest(user.Position.Y + mouseDiff.Y, Submarine.GridSize.Y));
|
||||
MathUtils.RoundTowardsClosest(userPos.X + mouseDiff.X, Submarine.GridSize.X),
|
||||
MathUtils.RoundTowardsClosest(userPos.Y + mouseDiff.Y, Submarine.GridSize.Y));
|
||||
}
|
||||
|
||||
public override void UpdateBroken(float deltaTime, Camera cam)
|
||||
|
||||
@@ -95,6 +95,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
foreach (Limb l in character.AnimController.Limbs)
|
||||
{
|
||||
if (l.IsSevered) { continue; }
|
||||
if (l.type == LimbType.LeftFoot || l.type == LimbType.LeftThigh || l.type == LimbType.LeftLeg) { continue; }
|
||||
if (l.type == LimbType.Head || l.type == LimbType.Torso)
|
||||
{
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (PickingTime > 0.0f)
|
||||
{
|
||||
if (picker.PickingItem == null && PickingTime <= float.MaxValue)
|
||||
if ((picker.PickingItem == null || picker.PickingItem == item) && PickingTime <= float.MaxValue)
|
||||
{
|
||||
#if SERVER
|
||||
item.CreateServerEvent(this);
|
||||
@@ -114,10 +114,6 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
activePicker = picker;
|
||||
picker.PickingItem = item;
|
||||
|
||||
var leftHand = picker.AnimController.GetLimb(LimbType.LeftHand);
|
||||
var rightHand = picker.AnimController.GetLimb(LimbType.RightHand);
|
||||
|
||||
pickTimer = 0.0f;
|
||||
while (pickTimer < requiredTime && Screen.Selected != GameMain.SubEditorScreen)
|
||||
{
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
foreach (Limb limb in character.AnimController.Limbs)
|
||||
{
|
||||
if (limb.WearingItems.Find(w => w.WearableComponent.Item == this.item) == null) continue;
|
||||
if (limb.WearingItems.Find(w => w.WearableComponent.Item == item) == null) { continue; }
|
||||
limb.body.ApplyForce(propulsion, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
|
||||
}
|
||||
|
||||
|
||||
@@ -90,6 +90,7 @@ namespace Barotrauma.Items.Components
|
||||
return MathHelper.ToRadians(MathHelper.Lerp(Spread, UnskilledSpread, degreeOfFailure));
|
||||
}
|
||||
|
||||
private readonly List<Body> limbBodies = new List<Body>();
|
||||
public override bool Use(float deltaTime, Character character = null)
|
||||
{
|
||||
if (character == null || character.Removed) { return false; }
|
||||
@@ -104,9 +105,10 @@ namespace Barotrauma.Items.Components
|
||||
item.AiTarget.SightRange = item.AiTarget.MaxSightRange;
|
||||
}
|
||||
|
||||
List<Body> limbBodies = new List<Body>();
|
||||
limbBodies.Clear();
|
||||
foreach (Limb l in character.AnimController.Limbs)
|
||||
{
|
||||
if (l.IsSevered) { continue; }
|
||||
limbBodies.Add(l.body.FarseerBody);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private Vector2 debugRayStartPos, debugRayEndPos;
|
||||
|
||||
private readonly List<Body> ignoredBodies = new List<Body>();
|
||||
|
||||
[Serialize("Both", false, description: "Can the item be used in air, water or both.")]
|
||||
public UseEnvironment UsableIn
|
||||
{
|
||||
@@ -68,6 +70,12 @@ namespace Barotrauma.Items.Components
|
||||
[Serialize(false, false, description: "Can the item repair things through holes in walls.")]
|
||||
public bool RepairThroughHoles { get; set; }
|
||||
|
||||
[Serialize(true, false, description: "Can the item hit broken doors.")]
|
||||
public bool HitItems { get; set; }
|
||||
|
||||
[Serialize(false, false, description: "Can the item hit broken doors.")]
|
||||
public bool HitBrokenDoors { get; set; }
|
||||
|
||||
[Serialize(0.0f, false, description: "The probability of starting a fire somewhere along the ray fired from the barrel (for example, 0.1 = 10% chance to start a fire during a second of use).")]
|
||||
public float FireProbability { get; set; }
|
||||
|
||||
@@ -114,8 +122,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
item.IsShootable = true;
|
||||
// TODO: should define this in xml if we have repair tools that don't require aim to use
|
||||
item.RequireAimToUse = true;
|
||||
item.RequireAimToUse = element.Parent.GetAttributeBool("requireaimtouse", true);
|
||||
InitProjSpecific(element);
|
||||
}
|
||||
|
||||
@@ -124,16 +131,17 @@ namespace Barotrauma.Items.Components
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
activeTimer -= deltaTime;
|
||||
if (activeTimer <= 0.0f) IsActive = false;
|
||||
if (activeTimer <= 0.0f) { IsActive = false; }
|
||||
}
|
||||
|
||||
private List<Body> ignoredBodies = new List<Body>();
|
||||
public override bool Use(float deltaTime, Character character = null)
|
||||
{
|
||||
if (character == null || character.Removed) return false;
|
||||
if (item.RequireAimToUse && !character.IsKeyDown(InputType.Aim)) return false;
|
||||
if (character != null)
|
||||
{
|
||||
if (item.RequireAimToUse && !character.IsKeyDown(InputType.Aim)) { return false; }
|
||||
}
|
||||
|
||||
float degreeOfSuccess = DegreeOfSuccess(character);
|
||||
float degreeOfSuccess = character == null ? 0.5f : DegreeOfSuccess(character);
|
||||
|
||||
if (Rand.Range(0.0f, 0.5f) > degreeOfSuccess)
|
||||
{
|
||||
@@ -187,12 +195,15 @@ namespace Barotrauma.Items.Components
|
||||
(float)Math.Sin(angle)) * Range * item.body.Dir);
|
||||
|
||||
ignoredBodies.Clear();
|
||||
foreach (Limb limb in character.AnimController.Limbs)
|
||||
if (character != null)
|
||||
{
|
||||
if (Rand.Range(0.0f, 0.5f) > degreeOfSuccess) continue;
|
||||
ignoredBodies.Add(limb.body.FarseerBody);
|
||||
foreach (Limb limb in character.AnimController.Limbs)
|
||||
{
|
||||
if (Rand.Range(0.0f, 0.5f) > degreeOfSuccess) continue;
|
||||
ignoredBodies.Add(limb.body.FarseerBody);
|
||||
}
|
||||
ignoredBodies.Add(character.AnimController.Collider.FarseerBody);
|
||||
}
|
||||
ignoredBodies.Add(character.AnimController.Collider.FarseerBody);
|
||||
|
||||
IsActive = true;
|
||||
activeTimer = 0.1f;
|
||||
@@ -200,7 +211,8 @@ namespace Barotrauma.Items.Components
|
||||
debugRayStartPos = ConvertUnits.ToDisplayUnits(rayStart);
|
||||
debugRayEndPos = ConvertUnits.ToDisplayUnits(rayEnd);
|
||||
|
||||
if (character.Submarine == null)
|
||||
Submarine parentSub = character?.Submarine ?? item.Submarine;
|
||||
if (parentSub == null)
|
||||
{
|
||||
foreach (Submarine sub in Submarine.Loaded)
|
||||
{
|
||||
@@ -216,7 +228,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
Repair(rayStart - character.Submarine.SimPosition, rayEnd - character.Submarine.SimPosition, deltaTime, character, degreeOfSuccess, ignoredBodies);
|
||||
Repair(rayStart - parentSub.SimPosition, rayEnd - parentSub.SimPosition, deltaTime, character, degreeOfSuccess, ignoredBodies);
|
||||
}
|
||||
|
||||
UseProjSpecific(deltaTime, rayStart);
|
||||
@@ -314,6 +326,18 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (RepairThroughHoles && f.IsSensor && f.Body?.UserData is Structure) { return false; }
|
||||
if (f.Body?.UserData as string == "ruinroom") { return false; }
|
||||
if (f.Body?.UserData is Item targetItem)
|
||||
{
|
||||
if (!HitItems) { return false; }
|
||||
if (HitBrokenDoors)
|
||||
{
|
||||
if (targetItem.GetComponent<Door>() == null && targetItem.Condition <= 0) { return false; }
|
||||
}
|
||||
else
|
||||
{
|
||||
if (targetItem.Condition <= 0) { return false; }
|
||||
}
|
||||
}
|
||||
return f.Body?.UserData != null;
|
||||
},
|
||||
allowInsideFixture: true));
|
||||
@@ -440,7 +464,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else if (targetBody.UserData is Item targetItem)
|
||||
{
|
||||
|
||||
if (!HitItems) { return false; }
|
||||
|
||||
var levelResource = targetItem.GetComponent<LevelResource>();
|
||||
if (levelResource != null && levelResource.Attached &&
|
||||
levelResource.requiredItems.Any() &&
|
||||
@@ -459,6 +484,15 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (!targetItem.Prefab.DamagedByRepairTools) { return false; }
|
||||
|
||||
if (HitBrokenDoors)
|
||||
{
|
||||
if (targetItem.GetComponent<Door>() == null && targetItem.Condition <= 0) { return false; }
|
||||
}
|
||||
else
|
||||
{
|
||||
if (targetItem.Condition <= 0) { return false; }
|
||||
}
|
||||
|
||||
targetItem.IsHighlighted = true;
|
||||
|
||||
ApplyStatusEffectsOnTarget(user, deltaTime, ActionType.OnUse, targetItem.AllPropertyObjects);
|
||||
@@ -647,26 +681,26 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
if (user == null) { return; }
|
||||
// Hard-coded progress bars for welding doors stuck.
|
||||
// A general purpose system could be better, but it would most likely require changes in the way we define the status effects in xml.
|
||||
foreach (ISerializableEntity target in targets)
|
||||
{
|
||||
if (target is Door door)
|
||||
if (!(target is Door door)) { continue; }
|
||||
|
||||
if (!door.CanBeWelded) { continue; }
|
||||
for (int i = 0; i < effect.propertyNames.Length; i++)
|
||||
{
|
||||
if (!door.CanBeWelded) continue;
|
||||
for (int i = 0; i < effect.propertyNames.Length; i++)
|
||||
string propertyName = effect.propertyNames[i];
|
||||
if (propertyName != "stuck") { continue; }
|
||||
if (door.SerializableProperties == null || !door.SerializableProperties.TryGetValue(propertyName, out SerializableProperty property)) { continue; }
|
||||
object value = property.GetValue(target);
|
||||
if (door.Stuck > 0)
|
||||
{
|
||||
string propertyName = effect.propertyNames[i];
|
||||
if (propertyName != "stuck") { continue; }
|
||||
if (door.SerializableProperties == null || !door.SerializableProperties.TryGetValue(propertyName, out SerializableProperty property)) { continue; }
|
||||
object value = property.GetValue(target);
|
||||
if (door.Stuck > 0)
|
||||
{
|
||||
var progressBar = user.UpdateHUDProgressBar(door, door.Item.WorldPosition, door.Stuck / 100, Color.DarkGray * 0.5f, Color.White);
|
||||
if (progressBar != null) { progressBar.Size = new Vector2(60.0f, 20.0f); }
|
||||
}
|
||||
var progressBar = user.UpdateHUDProgressBar(door, door.Item.WorldPosition, door.Stuck / 100, Color.DarkGray * 0.5f, Color.White);
|
||||
if (progressBar != null) { progressBar.Size = new Vector2(60.0f, 20.0f); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -421,7 +421,10 @@ namespace Barotrauma.Items.Components
|
||||
case "activate":
|
||||
case "use":
|
||||
case "trigger_in":
|
||||
item.Use(1.0f, sender);
|
||||
if (signal != "0")
|
||||
{
|
||||
item.Use(1.0f, sender);
|
||||
}
|
||||
break;
|
||||
case "toggle":
|
||||
if (signal != "0")
|
||||
@@ -734,12 +737,15 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public virtual void Load(XElement componentElement, bool usePrefabValues)
|
||||
{
|
||||
if (componentElement != null && !usePrefabValues)
|
||||
if (componentElement != null)
|
||||
{
|
||||
foreach (XAttribute attribute in componentElement.Attributes())
|
||||
{
|
||||
if (!SerializableProperties.TryGetValue(attribute.Name.ToString().ToLowerInvariant(), out SerializableProperty property)) { continue; }
|
||||
property.TrySetValue(this, attribute.Value);
|
||||
if (property.OverridePrefabValues || !usePrefabValues)
|
||||
{
|
||||
property.TrySetValue(this, attribute.Value);
|
||||
}
|
||||
}
|
||||
ParseMsg();
|
||||
OverrideRequiredItems(componentElement);
|
||||
@@ -908,49 +914,56 @@ namespace Barotrauma.Items.Components
|
||||
#region AI related
|
||||
protected const float AIUpdateInterval = 0.2f;
|
||||
protected float aiUpdateTimer;
|
||||
|
||||
private int itemIndex;
|
||||
private List<Item> ignoredContainers = new List<Item>();
|
||||
private Character previousUser;
|
||||
protected bool FindSuitableContainer(Character character, Func<Item, float> priority, out Item suitableContainer)
|
||||
{
|
||||
if (previousUser != character)
|
||||
{
|
||||
ignoredContainers.Clear();
|
||||
previousUser = character;
|
||||
}
|
||||
suitableContainer = null;
|
||||
if (character.FindItem(ref itemIndex, out Item targetContainer, ignoredItems: ignoredContainers, customPriorityFunction: priority))
|
||||
if (character.AIController is HumanAIController aiController)
|
||||
{
|
||||
suitableContainer = targetContainer;
|
||||
return true;
|
||||
if (previousUser != character)
|
||||
{
|
||||
previousUser = character;
|
||||
itemIndex = 0;
|
||||
}
|
||||
if (character.FindItem(ref itemIndex, out Item targetContainer, ignoredItems: aiController.IgnoredItems, customPriorityFunction: priority))
|
||||
{
|
||||
suitableContainer = targetContainer;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected AIObjectiveContainItem AIContainItems<T>(ItemContainer container, Character character, AIObjective objective, int itemCount, bool equip, bool removeEmpty) where T : ItemComponent
|
||||
{
|
||||
var containObjective = new AIObjectiveContainItem(character, container.GetContainableItemIdentifiers.ToArray(), container, objective.objectiveManager)
|
||||
AIObjectiveContainItem containObjective = null;
|
||||
if (character.AIController is HumanAIController aiController)
|
||||
{
|
||||
targetItemCount = itemCount,
|
||||
Equip = equip,
|
||||
RemoveEmpty = removeEmpty,
|
||||
GetItemPriority = i =>
|
||||
containObjective = new AIObjectiveContainItem(character, container.GetContainableItemIdentifiers.ToArray(), container, objective.objectiveManager)
|
||||
{
|
||||
if (i.ParentInventory?.Owner is Item)
|
||||
targetItemCount = itemCount,
|
||||
Equip = equip,
|
||||
RemoveEmpty = removeEmpty,
|
||||
GetItemPriority = i =>
|
||||
{
|
||||
//don't take items from other items of the same type
|
||||
if (((Item)i.ParentInventory.Owner).GetComponent<T>() != null)
|
||||
if (i.ParentInventory?.Owner is Item)
|
||||
{
|
||||
return 0.0f;
|
||||
//don't take items from other items of the same type
|
||||
if (((Item)i.ParentInventory.Owner).GetComponent<T>() != null)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
return 1.0f;
|
||||
}
|
||||
};
|
||||
// TODO: are we sure that we want to abandon the objective here?
|
||||
containObjective.Abandoned += () => objective.Abandon = true;
|
||||
objective.AddSubObjective(containObjective);
|
||||
};
|
||||
containObjective.Abandoned += () =>
|
||||
{
|
||||
aiController.IgnoredItems.Add(container.Item);
|
||||
};
|
||||
objective.AddSubObjective(containObjective);
|
||||
}
|
||||
return containObjective;
|
||||
}
|
||||
|
||||
@@ -959,68 +972,71 @@ namespace Barotrauma.Items.Components
|
||||
/// </summary>
|
||||
protected bool AIDecontainEmptyItems(Character character, AIObjective objective, bool equip, ItemContainer sourceContainer = null)
|
||||
{
|
||||
ItemContainer sourceC = sourceContainer ?? (item.OwnInventory?.Owner is Item it ? it.GetComponent<ItemContainer>() : null);
|
||||
var containedItems = sourceContainer != null ? sourceContainer.Inventory.Items : item.OwnInventory.Items;
|
||||
foreach (Item containedItem in containedItems)
|
||||
if (character.AIController is HumanAIController aiController)
|
||||
{
|
||||
if (containedItem != null && containedItem.Condition <= 0.0f)
|
||||
ItemContainer sourceC = sourceContainer ?? (item.OwnInventory?.Owner is Item it ? it.GetComponent<ItemContainer>() : null);
|
||||
var containedItems = sourceContainer != null ? sourceContainer.Inventory.Items : item.OwnInventory.Items;
|
||||
foreach (Item containedItem in containedItems)
|
||||
{
|
||||
if (FindSuitableContainer(character,
|
||||
i =>
|
||||
{
|
||||
var container = i.GetComponent<ItemContainer>();
|
||||
if (container == null) { return 0; }
|
||||
if (container.Inventory.IsFull()) { return 0; }
|
||||
if (containedItem != null && containedItem.Condition <= 0.0f)
|
||||
{
|
||||
if (FindSuitableContainer(character,
|
||||
i =>
|
||||
{
|
||||
var container = i.GetComponent<ItemContainer>();
|
||||
if (container == null) { return 0; }
|
||||
if (container.Inventory.IsFull()) { return 0; }
|
||||
// Ignore containers that are identical to the source container
|
||||
if (sourceC != null && container.Item.Prefab == sourceC.Item.Prefab) { return 0; }
|
||||
if (container.ShouldBeContained(containedItem, out bool isRestrictionsDefined))
|
||||
{
|
||||
if (isRestrictionsDefined)
|
||||
if (container.ShouldBeContained(containedItem, out bool isRestrictionsDefined))
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (containedItem.Prefab.IsContainerPreferred(container, out bool isPreferencesDefined, out bool isSecondary))
|
||||
if (isRestrictionsDefined)
|
||||
{
|
||||
return isPreferencesDefined ? isSecondary ? 2 : 3 : 1;
|
||||
return 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
return isPreferencesDefined ? 0 : 1;
|
||||
if (containedItem.Prefab.IsContainerPreferred(container, out bool isPreferencesDefined, out bool isSecondary))
|
||||
{
|
||||
return isPreferencesDefined ? isSecondary ? 2 : 3 : 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return isPreferencesDefined ? 0 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}, out Item targetContainer))
|
||||
{
|
||||
var decontainObjective = new AIObjectiveDecontainItem(character, containedItem, objective.objectiveManager, sourceC, targetContainer?.GetComponent<ItemContainer>())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}, out Item targetContainer))
|
||||
{
|
||||
var decontainObjective = new AIObjectiveDecontainItem(character, containedItem, objective.objectiveManager, sourceC, targetContainer?.GetComponent<ItemContainer>())
|
||||
{
|
||||
Equip = equip
|
||||
};
|
||||
decontainObjective.Abandoned += () =>
|
||||
{
|
||||
itemIndex = 0;
|
||||
if (targetContainer != null)
|
||||
{
|
||||
ignoredContainers.Add(targetContainer);
|
||||
}
|
||||
};
|
||||
decontainObjective.Completed += () =>
|
||||
{
|
||||
if (targetContainer == null)
|
||||
Equip = equip
|
||||
};
|
||||
decontainObjective.Abandoned += () =>
|
||||
{
|
||||
itemIndex = 0;
|
||||
}
|
||||
};
|
||||
objective.AddSubObjectiveInQueue(decontainObjective);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
if (targetContainer != null)
|
||||
{
|
||||
aiController.IgnoredItems.Add(targetContainer);
|
||||
}
|
||||
};
|
||||
decontainObjective.Completed += () =>
|
||||
{
|
||||
if (targetContainer == null)
|
||||
{
|
||||
itemIndex = 0;
|
||||
}
|
||||
};
|
||||
objective.AddSubObjectiveInQueue(decontainObjective);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,6 +80,13 @@ namespace Barotrauma.Items.Components
|
||||
set { itemRotation = MathHelper.ToRadians(value); }
|
||||
}
|
||||
|
||||
[Serialize("", false, description: "Specify an item for the container to spawn with.")]
|
||||
public string SpawnWithId
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public bool ShouldBeContained(string[] identifiersOrTags, out bool isRestrictionsDefined)
|
||||
{
|
||||
isRestrictionsDefined = containableRestrictions.Any();
|
||||
@@ -143,7 +150,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
//no need to Update() if this item has no statuseffects and no physics body
|
||||
IsActive = itemsWithStatusEffects.Count > 0 || containedItem.body != null;
|
||||
IsActive = itemsWithStatusEffects.Count > 0 || Inventory.Items.Any(it => it?.body != null);
|
||||
}
|
||||
|
||||
public void OnItemRemoved(Item containedItem)
|
||||
@@ -151,7 +158,7 @@ namespace Barotrauma.Items.Components
|
||||
itemsWithStatusEffects.RemoveAll(i => i.First == containedItem);
|
||||
|
||||
//deactivate if the inventory is empty
|
||||
IsActive = itemsWithStatusEffects.Count > 0 || containedItem.body != null;
|
||||
IsActive = itemsWithStatusEffects.Count > 0 || Inventory.Items.Any(it => it?.body != null);
|
||||
}
|
||||
|
||||
public bool CanBeContained(Item item)
|
||||
@@ -200,6 +207,22 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnItemLoaded()
|
||||
{
|
||||
base.OnItemLoaded();
|
||||
if (SpawnWithId.Length > 0)
|
||||
{
|
||||
ItemPrefab prefab = ItemPrefab.Prefabs.Find(m => m.Identifier == SpawnWithId);
|
||||
if (prefab != null)
|
||||
{
|
||||
if (Inventory != null && Inventory.Items.Any(it => it == null))
|
||||
{
|
||||
Entity.Spawner?.AddToSpawnQueue(prefab, Inventory);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool HasRequiredItems(Character character, bool addMessage, string msg = null)
|
||||
{
|
||||
return (!AccessOnlyWhenBroken || Item.Condition <= 0) && base.HasRequiredItems(character, addMessage, msg);
|
||||
@@ -295,7 +318,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
foreach (Item contained in Inventory.Items)
|
||||
{
|
||||
if (contained == null) continue;
|
||||
if (contained == null) { continue; }
|
||||
if (contained.body != null)
|
||||
{
|
||||
try
|
||||
@@ -311,6 +334,7 @@ namespace Barotrauma.Items.Components
|
||||
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
|
||||
"SetTransformIgnoreContacts threw an exception in SetContainedItemPositions (" + e.Message + ")\n" + e.StackTrace);
|
||||
}
|
||||
contained.body.Submarine = item.Submarine;
|
||||
}
|
||||
|
||||
contained.Rect =
|
||||
|
||||
@@ -39,8 +39,6 @@ namespace Barotrauma.Items.Components
|
||||
private Item focusTarget;
|
||||
private float targetRotation;
|
||||
|
||||
private bool state;
|
||||
|
||||
public Vector2 UserPos
|
||||
{
|
||||
get { return userPos; }
|
||||
@@ -61,6 +59,18 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, false, description: "Whether the item is toggled on/off. Only valid if IsToggle is set to true.")]
|
||||
public bool State
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public bool ControlCharacterPose
|
||||
{
|
||||
get { return limbPositions.Count > 0; }
|
||||
}
|
||||
|
||||
public Controller(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
@@ -99,7 +109,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (IsToggle)
|
||||
{
|
||||
item.SendSignal(0, state ? "1" : "0", "signal_out", sender: null);
|
||||
item.SendSignal(0, State ? "1" : "0", "signal_out", sender: null);
|
||||
}
|
||||
|
||||
if (user == null
|
||||
@@ -272,7 +282,7 @@ namespace Barotrauma.Items.Components
|
||||
return true;
|
||||
}
|
||||
|
||||
private Item GetFocusTarget()
|
||||
public Item GetFocusTarget()
|
||||
{
|
||||
item.SendSignal(0, MathHelper.ToDegrees(targetRotation).ToString("G", CultureInfo.InvariantCulture), "position_out", user);
|
||||
|
||||
@@ -294,7 +304,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
state = !state;
|
||||
State = !State;
|
||||
#if SERVER
|
||||
item.CreateServerEvent(this);
|
||||
#endif
|
||||
|
||||
@@ -113,7 +113,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
if (targetItem.Prefab.DeconstructItems.Any())
|
||||
if (targetItem.Prefab.AllowDeconstruct)
|
||||
{
|
||||
//drop all items that are inside the deconstructed item
|
||||
foreach (ItemContainer ic in targetItem.GetComponents<ItemContainer>())
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Barotrauma.Items.Components
|
||||
private float prevVoltage;
|
||||
|
||||
private float controlLockTimer;
|
||||
|
||||
|
||||
[Editable(0.0f, 10000000.0f),
|
||||
Serialize(2000.0f, true, description: "The amount of force exerted on the submarine when the engine is operating at full power.")]
|
||||
public float MaxForce
|
||||
@@ -93,8 +93,8 @@ namespace Barotrauma.Items.Components
|
||||
controlLockTimer -= deltaTime;
|
||||
|
||||
currPowerConsumption = Math.Abs(targetForce) / 100.0f * powerConsumption;
|
||||
//pumps consume more power when in a bad condition
|
||||
currPowerConsumption *= MathHelper.Lerp(1.5f, 1.0f, item.Condition / item.MaxCondition);
|
||||
//engines consume more power when in a bad condition
|
||||
item.GetComponent<Repairable>()?.AdjustPowerConsumption(ref currPowerConsumption);
|
||||
|
||||
if (powerConsumption == 0.0f) { Voltage = 1.0f; }
|
||||
|
||||
@@ -119,12 +119,15 @@ namespace Barotrauma.Items.Components
|
||||
float max = 1 + maxChangeSpeed;
|
||||
UpdateAITargets(Math.Clamp(noise, min, max), deltaTime);
|
||||
#if CLIENT
|
||||
for (int i = 0; i < 5; i++)
|
||||
particleTimer -= deltaTime;
|
||||
if (particleTimer <= 0.0f)
|
||||
{
|
||||
Vector2 particleVel = -currForce.ClampLength(5000.0f) / 5.0f;
|
||||
GameMain.ParticleManager.CreateParticle("bubbles", item.WorldPosition + PropellerPos,
|
||||
-currForce / 5.0f + new Vector2(Rand.Range(-100.0f, 100.0f), Rand.Range(-50f, 50f)),
|
||||
particleVel * Rand.Range(0.9f, 1.1f),
|
||||
0.0f, item.CurrentHull);
|
||||
}
|
||||
particleTimer = 1.0f / particlesPerSec;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ namespace Barotrauma.Items.Components
|
||||
outputContainer.Inventory.Locked = true;
|
||||
|
||||
currPowerConsumption = powerConsumption;
|
||||
currPowerConsumption *= MathHelper.Lerp(1.5f, 1.0f, item.Condition / item.MaxCondition);
|
||||
item.GetComponent<Repairable>()?.AdjustPowerConsumption(ref currPowerConsumption);
|
||||
|
||||
if (GameMain.NetworkMember?.IsServer ?? true)
|
||||
{
|
||||
|
||||
+2
-2
@@ -22,7 +22,7 @@ namespace Barotrauma.Items.Components
|
||||
private set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(400.0f, true, description: "How much oxygen the machine generates when operating at full power.")]
|
||||
[Editable, Serialize(400.0f, true, description: "How much oxygen the machine generates when operating at full power.", alwaysUseInstanceValues: true)]
|
||||
public float GeneratedAmount
|
||||
{
|
||||
get { return generatedAmount; }
|
||||
@@ -42,7 +42,7 @@ namespace Barotrauma.Items.Components
|
||||
CurrFlow = 0.0f;
|
||||
currPowerConsumption = powerConsumption;
|
||||
//consume more power when in a bad condition
|
||||
currPowerConsumption *= MathHelper.Lerp(1.5f, 1.0f, item.Condition / item.MaxCondition);
|
||||
item.GetComponent<Repairable>()?.AdjustPowerConsumption(ref currPowerConsumption);
|
||||
|
||||
if (powerConsumption <= 0.0f)
|
||||
{
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize(80.0f, false, description: "How fast the item pumps water in/out when operating at 100%.")]
|
||||
[Editable, Serialize(80.0f, false, description: "How fast the item pumps water in/out when operating at 100%.", alwaysUseInstanceValues: true)]
|
||||
public float MaxFlow
|
||||
{
|
||||
get { return maxFlow; }
|
||||
@@ -45,6 +45,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
public bool HasPower => IsActive && Voltage >= MinVoltage;
|
||||
public bool IsAutoControlled => pumpSpeedLockTimer > 0.0f || isActiveLockTimer > 0.0f;
|
||||
|
||||
public Pump(Item item, XElement element)
|
||||
: base(item, element)
|
||||
@@ -68,7 +69,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
currPowerConsumption = powerConsumption * Math.Abs(flowPercentage / 100.0f);
|
||||
//pumps consume more power when in a bad condition
|
||||
currPowerConsumption *= MathHelper.Lerp(1.5f, 1.0f, item.Condition / item.MaxCondition);
|
||||
item.GetComponent<Repairable>()?.AdjustPowerConsumption(ref currPowerConsumption);
|
||||
|
||||
if (!HasPower) { return; }
|
||||
|
||||
@@ -130,7 +131,7 @@ namespace Barotrauma.Items.Components
|
||||
if (objective.Option.Equals("stoppumping", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
#if SERVER
|
||||
if (FlowPercentage > 0.0f)
|
||||
if (objective.Override || FlowPercentage > 0.0f)
|
||||
{
|
||||
item.CreateServerEvent(this);
|
||||
}
|
||||
@@ -141,7 +142,7 @@ namespace Barotrauma.Items.Components
|
||||
else
|
||||
{
|
||||
#if SERVER
|
||||
if (!IsActive || FlowPercentage > -100.0f)
|
||||
if (objective.Override || !IsActive || FlowPercentage > -100.0f)
|
||||
{
|
||||
item.CreateServerEvent(this);
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Editable(0.0f, float.MaxValue), Serialize(10000.0f, true, description: "How much power (kW) the reactor generates when operating at full capacity.")]
|
||||
[Editable(0.0f, float.MaxValue), Serialize(10000.0f, true, description: "How much power (kW) the reactor generates when operating at full capacity.", alwaysUseInstanceValues: true)]
|
||||
public float MaxPowerOutput
|
||||
{
|
||||
get { return maxPowerOutput; }
|
||||
@@ -330,6 +330,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
item.SendSignal(0, ((int)(temperature * 100.0f)).ToString(), "temperature_out", null);
|
||||
item.SendSignal(0, ((int)-CurrPowerConsumption).ToString(), "power_value_out", null);
|
||||
item.SendSignal(0, ((int)load).ToString(), "load_value_out", null);
|
||||
|
||||
UpdateFailures(deltaTime);
|
||||
#if CLIENT
|
||||
@@ -622,7 +624,8 @@ namespace Barotrauma.Items.Components
|
||||
AutoTemp = false;
|
||||
targetFissionRate = 0.0f;
|
||||
targetTurbineOutput = 0.0f;
|
||||
break;
|
||||
unsentChanges = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (autoTemp != prevAutoTemp ||
|
||||
@@ -653,17 +656,23 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
break;
|
||||
case "set_fissionrate":
|
||||
if (float.TryParse(signal, NumberStyles.Float, CultureInfo.InvariantCulture, out float newFissionRate))
|
||||
if (PowerOn && float.TryParse(signal, NumberStyles.Float, CultureInfo.InvariantCulture, out float newFissionRate))
|
||||
{
|
||||
FissionRate = newFissionRate;
|
||||
targetFissionRate = newFissionRate;
|
||||
unsentChanges = true;
|
||||
#if CLIENT
|
||||
FissionRateScrollBar.BarScroll = targetFissionRate / 100.0f;
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case "set_turbineoutput":
|
||||
if (float.TryParse(signal, NumberStyles.Float, CultureInfo.InvariantCulture, out float newTurbineOutput))
|
||||
if (PowerOn && float.TryParse(signal, NumberStyles.Float, CultureInfo.InvariantCulture, out float newTurbineOutput))
|
||||
{
|
||||
TurbineOutput = newTurbineOutput;
|
||||
targetTurbineOutput = newTurbineOutput;
|
||||
unsentChanges = true;
|
||||
#if CLIENT
|
||||
TurbineOutputScrollBar.BarScroll = targetTurbineOutput / 100.0f;
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -108,10 +108,17 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public Vector2 TargetVelocity
|
||||
{
|
||||
get { return targetVelocity;}
|
||||
set
|
||||
get { return targetVelocity; }
|
||||
set
|
||||
{
|
||||
if (!MathUtils.IsValid(value)) return;
|
||||
if (!MathUtils.IsValid(value))
|
||||
{
|
||||
if (!MathUtils.IsValid(targetVelocity))
|
||||
{
|
||||
targetVelocity = Vector2.Zero;
|
||||
}
|
||||
return;
|
||||
}
|
||||
targetVelocity.X = MathHelper.Clamp(value.X, -100.0f, 100.0f);
|
||||
targetVelocity.Y = MathHelper.Clamp(value.Y, -100.0f, 100.0f);
|
||||
}
|
||||
@@ -285,16 +292,13 @@ namespace Barotrauma.Items.Components
|
||||
if (AutoPilot)
|
||||
{
|
||||
UpdateAutoPilot(deltaTime);
|
||||
targetVelocity = targetVelocity.ClampLength(MathHelper.Lerp(AutoPilotMaxSpeed, AIPilotMaxSpeed, userSkill) * 100.0f);
|
||||
TargetVelocity = TargetVelocity.ClampLength(MathHelper.Lerp(AutoPilotMaxSpeed, AIPilotMaxSpeed, userSkill) * 100.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (user != null && user.Info != null && user.SelectedConstruction == item)
|
||||
{
|
||||
user.Info.IncreaseSkillLevel(
|
||||
"helm",
|
||||
SkillSettings.Current.SkillIncreasePerSecondWhenSteering / Math.Max(userSkill, 1.0f) * deltaTime,
|
||||
user.WorldPosition + Vector2.UnitY * 150.0f);
|
||||
IncreaseSkillLevel(user, deltaTime);
|
||||
}
|
||||
|
||||
Vector2 velocityDiff = steeringInput - targetVelocity;
|
||||
@@ -323,6 +327,18 @@ namespace Barotrauma.Items.Components
|
||||
item.SendSignal(0, targetLevel.ToString(CultureInfo.InvariantCulture), "velocity_y_out", null);
|
||||
}
|
||||
|
||||
private void IncreaseSkillLevel(Character user, float deltaTime)
|
||||
{
|
||||
if (user?.Info == null) { return; }
|
||||
|
||||
float userSkill = user.GetSkillLevel("helm") / 100.0f;
|
||||
user.Info.IncreaseSkillLevel(
|
||||
"helm",
|
||||
SkillSettings.Current.SkillIncreasePerSecondWhenSteering / Math.Max(userSkill, 1.0f) * deltaTime,
|
||||
user.WorldPosition + Vector2.UnitY * 150.0f);
|
||||
|
||||
}
|
||||
|
||||
private void UpdateAutoPilot(float deltaTime)
|
||||
{
|
||||
if (controlledSub == null) { return; }
|
||||
@@ -443,41 +459,46 @@ namespace Barotrauma.Items.Components
|
||||
//steer away from other subs
|
||||
foreach (Submarine sub in Submarine.Loaded)
|
||||
{
|
||||
if (sub == controlledSub) continue;
|
||||
if (controlledSub.DockedTo.Contains(sub)) continue;
|
||||
|
||||
float thisSize = Math.Max(controlledSub.Borders.Width, controlledSub.Borders.Height);
|
||||
float otherSize = Math.Max(sub.Borders.Width, sub.Borders.Height);
|
||||
|
||||
if (sub == controlledSub) { continue; }
|
||||
if (controlledSub.DockedTo.Contains(sub)) { continue; }
|
||||
Point sizeSum = controlledSub.Borders.Size + sub.Borders.Size;
|
||||
Vector2 minDist = sizeSum.ToVector2() / 2;
|
||||
Vector2 diff = controlledSub.WorldPosition - sub.WorldPosition;
|
||||
float dist = diff == Vector2.Zero ? 0.0f : diff.Length();
|
||||
|
||||
//far enough -> ignore
|
||||
if (dist > thisSize + otherSize) continue;
|
||||
|
||||
Vector2 dir = dist <= 0.0001f ? Vector2.UnitY : diff / dist;
|
||||
float dot = controlledSub.Velocity == Vector2.Zero ?
|
||||
0.0f : Vector2.Dot(Vector2.Normalize(controlledSub.Velocity), -dir);
|
||||
|
||||
//heading away -> ignore
|
||||
if (dot < 0.0f) continue;
|
||||
|
||||
targetVelocity += diff * 200.0f;
|
||||
float xDist = Math.Abs(diff.X);
|
||||
float yDist = Math.Abs(diff.Y);
|
||||
Vector2 maxAvoidDistance = minDist * 2;
|
||||
if (xDist > maxAvoidDistance.X || yDist > maxAvoidDistance.Y)
|
||||
{
|
||||
//far enough -> ignore
|
||||
continue;
|
||||
}
|
||||
float dot = controlledSub.Velocity == Vector2.Zero ? 0.0f : Vector2.Dot(Vector2.Normalize(controlledSub.Velocity), -diff);
|
||||
if (dot < 0.0f)
|
||||
{
|
||||
//heading away -> ignore
|
||||
continue;
|
||||
}
|
||||
float distanceFactor = MathHelper.Lerp(0, 1, MathUtils.InverseLerp(maxAvoidDistance.X + maxAvoidDistance.Y, minDist.X + minDist.Y, xDist + yDist));
|
||||
float velocityFactor = MathHelper.Lerp(0, 1, MathUtils.InverseLerp(0, 3, controlledSub.Velocity.Length()));
|
||||
TargetVelocity += 100 * Vector2.Normalize(diff) * distanceFactor * velocityFactor;
|
||||
}
|
||||
|
||||
//clamp velocity magnitude to 100.0f
|
||||
float velMagnitude = targetVelocity.Length();
|
||||
//clamp velocity magnitude to 100.0f (Is this required? The X and Y components are clamped in the property setter)
|
||||
float velMagnitude = TargetVelocity.Length();
|
||||
if (velMagnitude > 100.0f)
|
||||
{
|
||||
targetVelocity *= 100.0f / velMagnitude;
|
||||
TargetVelocity *= 100.0f / velMagnitude;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdatePath()
|
||||
{
|
||||
if (Level.Loaded == null) { return; }
|
||||
|
||||
if (pathFinder == null) pathFinder = new PathFinder(WayPoint.WayPointList, false);
|
||||
|
||||
if (pathFinder == null)
|
||||
{
|
||||
pathFinder = new PathFinder(WayPoint.WayPointList, false);
|
||||
}
|
||||
|
||||
Vector2 target;
|
||||
if (LevelEndSelected)
|
||||
@@ -553,6 +574,7 @@ namespace Barotrauma.Items.Components
|
||||
unsentChanges = true;
|
||||
AutoPilot = true;
|
||||
}
|
||||
IncreaseSkillLevel(user, deltaTime);
|
||||
switch (objective.Option.ToLowerInvariant())
|
||||
{
|
||||
case "maintainposition":
|
||||
|
||||
@@ -316,6 +316,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (recipient.Item == item || recipient.Item == source) { continue; }
|
||||
|
||||
source?.LastSentSignalRecipients.Add(recipient.Item);
|
||||
|
||||
foreach (ItemComponent ic in recipient.Item.Components)
|
||||
{
|
||||
//other junction boxes don't need to receive the signal in the pass-through signal connections
|
||||
|
||||
@@ -241,7 +241,6 @@ namespace Barotrauma.Items.Components
|
||||
private void Launch(Vector2 impulse)
|
||||
{
|
||||
hits.Clear();
|
||||
MaxTargetsToHit = 2;
|
||||
|
||||
if (item.AiTarget != null)
|
||||
{
|
||||
@@ -487,6 +486,10 @@ namespace Barotrauma.Items.Components
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (target.Body.UserData is Item item)
|
||||
{
|
||||
if (item.Condition <= 0.0f) { return false; }
|
||||
}
|
||||
|
||||
//ignore character colliders (the projectile only hits limbs)
|
||||
if (target.CollisionCategories == Physics.CollisionCharacter && target.Body.UserData is Character)
|
||||
@@ -634,6 +637,7 @@ namespace Barotrauma.Items.Components
|
||||
item.body.LinearVelocity *= 0.1f;
|
||||
}
|
||||
else if (Vector2.Dot(velocity, collisionNormal) < 0.0f && hits.Count() >= MaxTargetsToHit &&
|
||||
target.Body.Mass > item.body.Mass * 0.5f &&
|
||||
(DoesStick ||
|
||||
(StickToCharacters && target.Body.UserData is Limb) ||
|
||||
(StickToStructures && target.Body.UserData is Structure) ||
|
||||
|
||||
@@ -55,8 +55,8 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(80.0f, true, description: "The condition of the item has to be below this for AI characters to repair it. Percentages of max condition."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f)]
|
||||
public float AIRepairThreshold
|
||||
[Serialize(80.0f, true, description: "The condition of the item has to be below this for it to become repairable. Percentages of max condition."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f)]
|
||||
public float RepairThreshold
|
||||
{
|
||||
get;
|
||||
set;
|
||||
@@ -112,13 +112,14 @@ namespace Barotrauma.Items.Components
|
||||
element.GetAttributeString("name", "");
|
||||
|
||||
//backwards compatibility
|
||||
var showRepairUIAttribute = element.Attributes().FirstOrDefault(a => a.Name.ToString().Equals("showrepairuithreshold", StringComparison.OrdinalIgnoreCase));
|
||||
if (showRepairUIAttribute != null)
|
||||
var repairThresholdAttribute =
|
||||
element.Attributes().FirstOrDefault(a => a.Name.ToString().Equals("showrepairuithreshold", StringComparison.OrdinalIgnoreCase)) ??
|
||||
element.Attributes().FirstOrDefault(a => a.Name.ToString().Equals("airepairth44reshold", StringComparison.OrdinalIgnoreCase));
|
||||
if (repairThresholdAttribute != null)
|
||||
{
|
||||
float repairThreshold;
|
||||
if (Single.TryParse(showRepairUIAttribute.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out repairThreshold))
|
||||
if (float.TryParse(repairThresholdAttribute.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out float repairThreshold))
|
||||
{
|
||||
AIRepairThreshold = repairThreshold;
|
||||
RepairThreshold = repairThreshold;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,7 +274,7 @@ namespace Barotrauma.Items.Components
|
||||
float successFactor = requiredSkills.Count == 0 ? 1.0f : DegreeOfSuccess(CurrentFixer, requiredSkills);
|
||||
|
||||
//item must have been below the repair threshold for the player to get an achievement or XP for repairing it
|
||||
if (!item.IsFullCondition)
|
||||
if (item.ConditionPercentage < RepairThreshold)
|
||||
{
|
||||
wasBroken = true;
|
||||
}
|
||||
@@ -309,11 +310,10 @@ namespace Barotrauma.Items.Components
|
||||
SkillSettings.Current.SkillIncreasePerRepair / Math.Max(characterSkillLevel, 1.0f),
|
||||
CurrentFixer.WorldPosition + Vector2.UnitY * 100.0f);
|
||||
}
|
||||
|
||||
SteamAchievementManager.OnItemRepaired(item, CurrentFixer);
|
||||
deteriorationTimer = Rand.Range(MinDeteriorationDelay, MaxDeteriorationDelay);
|
||||
wasBroken = false;
|
||||
}
|
||||
deteriorationTimer = Rand.Range(MinDeteriorationDelay, MaxDeteriorationDelay);
|
||||
wasBroken = false;
|
||||
StopRepairing(CurrentFixer);
|
||||
}
|
||||
}
|
||||
@@ -358,6 +358,14 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime);
|
||||
|
||||
public void AdjustPowerConsumption(ref float powerConsumption)
|
||||
{
|
||||
if (item.ConditionPercentage < RepairThreshold)
|
||||
{
|
||||
powerConsumption *= MathHelper.Lerp(1.5f, 1.0f, item.Condition / item.MaxCondition);
|
||||
}
|
||||
}
|
||||
|
||||
private bool ShouldDeteriorate()
|
||||
{
|
||||
if (LastActiveTime > Timing.TotalTime) { return true; }
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Barotrauma.Items.Components
|
||||
//the output is sent if both inputs have received a signal within the timeframe
|
||||
protected float timeFrame;
|
||||
|
||||
[InGameEditable(DecimalCount = 2), Serialize(0.0f, true, description: "The item sends the output if both inputs have received a non-zero signal within the timeframe. If set to 0, the inputs must receive a signal at the same time.")]
|
||||
[InGameEditable(DecimalCount = 2), Serialize(0.0f, true, description: "The item sends the output if both inputs have received a non-zero signal within the timeframe. If set to 0, the inputs must receive a signal at the same time.", alwaysUseInstanceValues: true)]
|
||||
public float TimeFrame
|
||||
{
|
||||
get { return timeFrame; }
|
||||
@@ -23,14 +23,14 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[InGameEditable, Serialize("1", true, description: "The signal sent when both inputs have received a non-zero signal.")]
|
||||
[InGameEditable, Serialize("1", true, description: "The signal sent when the condition is met.", alwaysUseInstanceValues: true)]
|
||||
public string Output
|
||||
{
|
||||
get { return output; }
|
||||
set { output = value; }
|
||||
}
|
||||
|
||||
[InGameEditable, Serialize("", true, description: "The signal sent when both inputs have not received a non-zero signal (if empty, no signal is sent).")]
|
||||
[InGameEditable, Serialize("", true, description: "The signal sent when the condition is met (if empty, no signal is sent).", alwaysUseInstanceValues: true)]
|
||||
public string FalseOutput
|
||||
{
|
||||
get { return falseOutput; }
|
||||
|
||||
+4
-4
@@ -15,7 +15,7 @@ namespace Barotrauma.Items.Components
|
||||
//the output is sent if both inputs have received a signal within the timeframe
|
||||
protected float timeFrame;
|
||||
|
||||
[Serialize(999999.0f, true, description: "The output of the item is restricted below this value."),
|
||||
[Serialize(999999.0f, true, description: "The output of the item is restricted below this value.", alwaysUseInstanceValues: true),
|
||||
InGameEditable(MinValueFloat = -999999.0f, MaxValueFloat = 999999.0f)]
|
||||
public float ClampMax
|
||||
{
|
||||
@@ -23,7 +23,7 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(-999999.0f, true, description: "The output of the item is restricted above this value."),
|
||||
[Serialize(-999999.0f, true, description: "The output of the item is restricted above this value.", alwaysUseInstanceValues: true),
|
||||
InGameEditable(MinValueFloat = -999999.0f, MaxValueFloat = 999999.0f)]
|
||||
public float ClampMin
|
||||
{
|
||||
@@ -32,8 +32,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
[InGameEditable(DecimalCount = 2),
|
||||
Serialize(0.0f, true, description: "The item must have received signals to both inputs within this timeframe to output the sum of the signals." +
|
||||
" If set to 0, the inputs must be received at the same time.")]
|
||||
Serialize(0.0f, true, description: "The item must have received signals to both inputs within this timeframe to output the result." +
|
||||
" If set to 0, the inputs must be received at the same time.", alwaysUseInstanceValues: true)]
|
||||
public float TimeFrame
|
||||
{
|
||||
get { return timeFrame; }
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private List<ushort> disconnectedWireIds;
|
||||
|
||||
[Editable, Serialize(false, true, description: "Locked connection panels cannot be rewired in-game.")]
|
||||
[Editable, Serialize(false, true, description: "Locked connection panels cannot be rewired in-game.", alwaysUseInstanceValues: true)]
|
||||
public bool Locked
|
||||
{
|
||||
get;
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace Barotrauma.Items.Components
|
||||
private DelayedSignal prevQueuedSignal;
|
||||
|
||||
private float delay;
|
||||
[InGameEditable(MinValueFloat = 0.0f, MaxValueFloat = 60.0f, DecimalCount = 2), Serialize(1.0f, true, description: "How long the item delays the signals (in seconds).")]
|
||||
[InGameEditable(MinValueFloat = 0.0f, MaxValueFloat = 60.0f, DecimalCount = 2), Serialize(1.0f, true, description: "How long the item delays the signals (in seconds).", alwaysUseInstanceValues: true)]
|
||||
public float Delay
|
||||
{
|
||||
get { return delay; }
|
||||
@@ -43,14 +43,14 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[InGameEditable, Serialize(false, true, description: "Should the component discard previously received signals when a new one is received.")]
|
||||
[InGameEditable, Serialize(false, true, description: "Should the component discard previously received signals when a new one is received.", alwaysUseInstanceValues: true)]
|
||||
public bool ResetWhenSignalReceived
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[InGameEditable, Serialize(false, true, description: "Should the component discard previously received signals when the incoming signal changes.")]
|
||||
[InGameEditable, Serialize(false, true, description: "Should the component discard previously received signals when the incoming signal changes.", alwaysUseInstanceValues: true)]
|
||||
public bool ResetWhenDifferentSignalReceived
|
||||
{
|
||||
get;
|
||||
|
||||
@@ -15,21 +15,21 @@ namespace Barotrauma.Items.Components
|
||||
//the output is sent if both inputs have received a signal within the timeframe
|
||||
protected float timeFrame;
|
||||
|
||||
[InGameEditable, Serialize("1", true, description: "The signal this item outputs when the received signals are equal.")]
|
||||
[InGameEditable, Serialize("1", true, description: "The signal this item outputs when the condition is met.", alwaysUseInstanceValues: true)]
|
||||
public string Output
|
||||
{
|
||||
get { return output; }
|
||||
set { output = value; }
|
||||
}
|
||||
|
||||
[InGameEditable, Serialize("", true, description: "The signal this item outputs when the received signals are not equal.")]
|
||||
[InGameEditable, Serialize("", true, description: "The signal this item outputs when the condition is not met.", alwaysUseInstanceValues: true)]
|
||||
public string FalseOutput
|
||||
{
|
||||
get { return falseOutput; }
|
||||
set { falseOutput = value; }
|
||||
}
|
||||
|
||||
[InGameEditable(DecimalCount = 2), Serialize(0.0f, true, description: "The maximum amount of time between the received signals. If set to 0, the signals must be received at the same time.")]
|
||||
[InGameEditable(DecimalCount = 2), Serialize(0.0f, true, description: "The maximum amount of time between the received signals. If set to 0, the signals must be received at the same time.", alwaysUseInstanceValues: true)]
|
||||
public float TimeFrame
|
||||
{
|
||||
get { return timeFrame; }
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ namespace Barotrauma.Items.Components
|
||||
class ExponentiationComponent : ItemComponent
|
||||
{
|
||||
private float exponent;
|
||||
[InGameEditable, Serialize(1.0f, false, description: "The exponent of the operation.")]
|
||||
[InGameEditable, Serialize(1.0f, false, description: "The exponent of the operation.", alwaysUseInstanceValues: true)]
|
||||
public float Exponent
|
||||
{
|
||||
get
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ namespace Barotrauma.Items.Components
|
||||
SquareRoot
|
||||
}
|
||||
|
||||
[Serialize(FunctionType.Round, false, description: "Which kind of function to run the input through.")]
|
||||
[Serialize(FunctionType.Round, false, description: "Which kind of function to run the input through.", alwaysUseInstanceValues: true)]
|
||||
public FunctionType Function
|
||||
{
|
||||
get; set;
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public PhysicsBody ParentBody;
|
||||
|
||||
[Serialize(100.0f, true, description: "The range of the emitted light. Higher values are more performance-intensive."),
|
||||
[Serialize(100.0f, true, description: "The range of the emitted light. Higher values are more performance-intensive.", alwaysUseInstanceValues: true),
|
||||
Editable(MinValueFloat = 0.0f, MaxValueFloat = 2048.0f)]
|
||||
public float Range
|
||||
{
|
||||
@@ -43,7 +43,7 @@ namespace Barotrauma.Items.Components
|
||||
public float Rotation;
|
||||
|
||||
[Editable, Serialize(true, true, description: "Should structures cast shadows when light from this light source hits them. " +
|
||||
"Disabling shadows increases the performance of the game, and is recommended for lights with a short range.")]
|
||||
"Disabling shadows increases the performance of the game, and is recommended for lights with a short range.", alwaysUseInstanceValues: true)]
|
||||
public bool CastShadows
|
||||
{
|
||||
get { return castShadows; }
|
||||
@@ -57,7 +57,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, true, description: "Lights drawn behind submarines don't cast any shadows and are much faster to draw than shadow-casting lights. " +
|
||||
"It's recommended to enable this on decorative lights outside the submarine's hull.")]
|
||||
"It's recommended to enable this on decorative lights outside the submarine's hull.", alwaysUseInstanceValues: true)]
|
||||
public bool DrawBehindSubs
|
||||
{
|
||||
get { return drawBehindSubs; }
|
||||
@@ -70,7 +70,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, true, description: "Is the light currently on.")]
|
||||
[Editable, Serialize(false, true, description: "Is the light currently on.", alwaysUseInstanceValues: true)]
|
||||
public bool IsOn
|
||||
{
|
||||
get { return IsActive; }
|
||||
@@ -110,7 +110,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[InGameEditable, Serialize("255,255,255,255", true, description: "The color of the emitted light (R,G,B,A).")]
|
||||
[InGameEditable, Serialize("255,255,255,255", true, description: "The color of the emitted light (R,G,B,A).", alwaysUseInstanceValues: true)]
|
||||
public Color LightColor
|
||||
{
|
||||
get { return lightColor; }
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
class MemoryComponent : ItemComponent
|
||||
{
|
||||
[InGameEditable, Serialize("", true, description: "The currently stored signal the item outputs.")]
|
||||
[InGameEditable, Serialize("", true, description: "The currently stored signal the item outputs.", alwaysUseInstanceValues: true)]
|
||||
public string Value
|
||||
{
|
||||
get;
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Barotrauma.Items.Components
|
||||
class ModuloComponent : ItemComponent
|
||||
{
|
||||
private float modulus;
|
||||
[InGameEditable, Serialize(1.0f, false, description: "The modulus of the operation. Must be non-zero.")]
|
||||
[InGameEditable, Serialize(1.0f, false, description: "The modulus of the operation. Must be non-zero.", alwaysUseInstanceValues: true)]
|
||||
public float Modulus
|
||||
{
|
||||
get { return modulus; }
|
||||
|
||||
@@ -18,14 +18,14 @@ namespace Barotrauma.Items.Components
|
||||
[Serialize(false, false, description: "Has the item currently detected movement. Intended to be used by StatusEffect conditionals (setting this value in XML has no effect).")]
|
||||
public bool MotionDetected { get; set; }
|
||||
|
||||
[Editable, Serialize(false, true, description: "Should the sensor only detect the movement of humans?")]
|
||||
[Editable, Serialize(false, true, description: "Should the sensor only detect the movement of humans?", alwaysUseInstanceValues: true)]
|
||||
public bool OnlyHumans
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, true, description: "Should the sensor ignore the bodies of dead characters?")]
|
||||
[Editable, Serialize(false, true, description: "Should the sensor ignore the bodies of dead characters?", alwaysUseInstanceValues: true)]
|
||||
public bool IgnoreDead
|
||||
{
|
||||
get;
|
||||
@@ -33,7 +33,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
|
||||
[InGameEditable, Serialize(0.0f, true, description: "Horizontal detection range.")]
|
||||
[InGameEditable, Serialize(0.0f, true, description: "Horizontal detection range.", alwaysUseInstanceValues: true)]
|
||||
public float RangeX
|
||||
{
|
||||
get { return rangeX; }
|
||||
@@ -45,7 +45,7 @@ namespace Barotrauma.Items.Components
|
||||
#endif
|
||||
}
|
||||
}
|
||||
[InGameEditable, Serialize(0.0f, true, description: "Vertical movement detection range.")]
|
||||
[InGameEditable, Serialize(0.0f, true, description: "Vertical movement detection range.", alwaysUseInstanceValues: true)]
|
||||
public float RangeY
|
||||
{
|
||||
get { return rangeY; }
|
||||
@@ -67,13 +67,13 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[InGameEditable, Serialize("1", true, description: "The signal the item outputs when it has detected movement.")]
|
||||
[InGameEditable, Serialize("1", true, description: "The signal the item outputs when it has detected movement.", alwaysUseInstanceValues: true)]
|
||||
public string Output { get; set; }
|
||||
|
||||
[InGameEditable, Serialize("", true, description: "The signal the item outputs when it has not detected movement.")]
|
||||
[InGameEditable, Serialize("", true, description: "The signal the item outputs when it has not detected movement.", alwaysUseInstanceValues: true)]
|
||||
public string FalseOutput { get; set; }
|
||||
|
||||
[Editable(DecimalCount = 3), Serialize(0.01f, true, description: "How fast the objects within the detector's range have to be moving (in m/s).")]
|
||||
[Editable(DecimalCount = 3), Serialize(0.01f, true, description: "How fast the objects within the detector's range have to be moving (in m/s).", alwaysUseInstanceValues: true)]
|
||||
public float MinimumVelocity
|
||||
{
|
||||
get;
|
||||
@@ -132,7 +132,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
foreach (Limb limb in c.AnimController.Limbs)
|
||||
{
|
||||
if (limb.LinearVelocity.LengthSquared() <= MinimumVelocity * MinimumVelocity) continue;
|
||||
if (limb.IsSevered) { continue; }
|
||||
if (limb.LinearVelocity.LengthSquared() <= MinimumVelocity * MinimumVelocity) { continue; }
|
||||
if (MathUtils.CircleIntersectsRectangle(limb.WorldPosition, ConvertUnits.ToDisplayUnits(limb.body.GetMaxExtent()), detectRect))
|
||||
{
|
||||
MotionDetected = true;
|
||||
|
||||
+2
-2
@@ -23,14 +23,14 @@ namespace Barotrauma.Items.Components
|
||||
[InGameEditable, Serialize(WaveType.Pulse, true, description: "What kind of a signal the item outputs." +
|
||||
" Pulse: periodically sends out a signal of 1." +
|
||||
" Sine: sends out a sine wave oscillating between -1 and 1." +
|
||||
" Square: sends out a signal that alternates between 0 and 1.")]
|
||||
" Square: sends out a signal that alternates between 0 and 1.", alwaysUseInstanceValues: true)]
|
||||
public WaveType OutputType
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[InGameEditable(DecimalCount = 2), Serialize(1.0f, true, description: "How fast the signal oscillates, or how fast the pulses are sent (in Hz).")]
|
||||
[InGameEditable(DecimalCount = 2), Serialize(1.0f, true, description: "How fast the signal oscillates, or how fast the pulses are sent (in Hz).", alwaysUseInstanceValues: true)]
|
||||
public float Frequency
|
||||
{
|
||||
get { return frequency; }
|
||||
|
||||
+4
-4
@@ -16,16 +16,16 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private bool nonContinuousOutputSent;
|
||||
|
||||
[InGameEditable, Serialize("1", true, description: "The signal this item outputs when the received signal matches the regular expression.")]
|
||||
[InGameEditable, Serialize("1", true, description: "The signal this item outputs when the received signal matches the regular expression.", alwaysUseInstanceValues: true)]
|
||||
public string Output { get; set; }
|
||||
|
||||
[Serialize("0", true, description: "The signal this item outputs when the received signal does not match the regular expression.")]
|
||||
[Serialize("0", true, description: "The signal this item outputs when the received signal does not match the regular expression.", alwaysUseInstanceValues: true)]
|
||||
public string FalseOutput { get; set; }
|
||||
|
||||
[InGameEditable, Serialize(true, true, description: "Should the component keep sending the output even after it stops receiving a signal, or only send an output when it receives a signal.")]
|
||||
[InGameEditable, Serialize(true, true, description: "Should the component keep sending the output even after it stops receiving a signal, or only send an output when it receives a signal.", alwaysUseInstanceValues: true)]
|
||||
public bool ContinuousOutput { get; set; }
|
||||
|
||||
[InGameEditable, Serialize("", true, description: "The regular expression used to check the incoming signals.")]
|
||||
[InGameEditable, Serialize("", true, description: "The regular expression used to check the incoming signals.", alwaysUseInstanceValues: true)]
|
||||
public string Expression
|
||||
{
|
||||
get { return expression; }
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, true, description: "Can the relay currently pass power and signals through it.")]
|
||||
[Editable, Serialize(false, true, description: "Can the relay currently pass power and signals through it.", alwaysUseInstanceValues: true)]
|
||||
public bool IsOn
|
||||
{
|
||||
get
|
||||
|
||||
+3
-3
@@ -4,12 +4,12 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
class SignalCheckComponent : ItemComponent
|
||||
{
|
||||
[InGameEditable, Serialize("1", true, description: "The signal this item outputs when the received signal matches the target signal.")]
|
||||
[InGameEditable, Serialize("1", true, description: "The signal this item outputs when the received signal matches the target signal.", alwaysUseInstanceValues: true)]
|
||||
public string Output { get; set; }
|
||||
[InGameEditable, Serialize("0", true, description: "The signal this item outputs when the received signal does not match the target signal.")]
|
||||
[InGameEditable, Serialize("0", true, description: "The signal this item outputs when the received signal does not match the target signal.", alwaysUseInstanceValues: true)]
|
||||
public string FalseOutput { get; set; }
|
||||
|
||||
[InGameEditable, Serialize("", true, description: "The value to compare the received signals against.")]
|
||||
[InGameEditable, Serialize("", true, description: "The value to compare the received signals against.", alwaysUseInstanceValues: true)]
|
||||
public string TargetSignal { get; set; }
|
||||
|
||||
public SignalCheckComponent(Item item, XElement element)
|
||||
|
||||
@@ -1,25 +1,51 @@
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
class SmokeDetector : ItemComponent
|
||||
{
|
||||
[Serialize(50.0f, false, description: "How large the fire has to be for the detector to react to it.")]
|
||||
public float FireSizeThreshold
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
const float FireCheckInterval = 1.0f;
|
||||
private float fireCheckTimer;
|
||||
|
||||
private bool fireInRange;
|
||||
|
||||
[InGameEditable, Serialize("1", true, description: "The signal the item outputs when it has detected movement.", alwaysUseInstanceValues: true)]
|
||||
public string Output { get; set; }
|
||||
|
||||
[InGameEditable, Serialize("0", true, description: "The signal the item outputs when it has not detected movement.", alwaysUseInstanceValues: true)]
|
||||
public string FalseOutput { get; set; }
|
||||
|
||||
public SmokeDetector(Item item, XElement element)
|
||||
: base (item, element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
private bool IsFireInRange()
|
||||
{
|
||||
if (item.CurrentHull == null || item.InWater) { return false; }
|
||||
|
||||
var connectedHulls = item.CurrentHull.GetConnectedHulls(includingThis: true, searchDepth: 10, ignoreClosedGaps: true);
|
||||
foreach (Hull hull in connectedHulls)
|
||||
{
|
||||
foreach (FireSource fireSource in hull.FireSources)
|
||||
{
|
||||
if (fireSource.IsInDamageRange(item.WorldPosition, fireSource.DamageRange * 2.0f)) { return true; }
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
item.SendSignal(0, item.CurrentHull != null && item.CurrentHull.FireSources.Any(fs => fs.Size.X > FireSizeThreshold) ? "1" : "0", "signal_out", null);
|
||||
fireCheckTimer -= deltaTime;
|
||||
if (fireCheckTimer <= 0.0f)
|
||||
{
|
||||
fireInRange = IsFireInRange();
|
||||
fireCheckTimer = FireCheckInterval;
|
||||
}
|
||||
item.SendSignal(0, fireInRange ? "1" : "0", "signal_out", null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
private string welcomeMessage;
|
||||
[InGameEditable, Serialize("", true, "Message to be displayed on the terminal display when it is first opened.", translationTextTag = "terminalwelcomemsg.")]
|
||||
[InGameEditable, Serialize("", true, "Message to be displayed on the terminal display when it is first opened.", translationTextTag = "terminalwelcomemsg.", AlwaysUseInstanceValues = true)]
|
||||
public string WelcomeMessage
|
||||
{
|
||||
get { return welcomeMessage; }
|
||||
|
||||
+2
-2
@@ -19,14 +19,14 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
protected float[] receivedSignal = new float[2];
|
||||
|
||||
[Serialize(FunctionType.Sin, false, description: "Which kind of function to run the input through.")]
|
||||
[Serialize(FunctionType.Sin, false, description: "Which kind of function to run the input through.", alwaysUseInstanceValues: true)]
|
||||
public FunctionType Function
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
|
||||
[InGameEditable, Serialize(false, true, description: "If set to true, the trigonometric function uses radians instead of degrees.")]
|
||||
[InGameEditable, Serialize(false, true, description: "If set to true, the trigonometric function uses radians instead of degrees.", alwaysUseInstanceValues: true)]
|
||||
public bool UseRadians
|
||||
{
|
||||
get; set;
|
||||
|
||||
@@ -10,10 +10,10 @@ namespace Barotrauma.Items.Components
|
||||
private bool isInWater;
|
||||
private float stateSwitchDelay;
|
||||
|
||||
[InGameEditable, Serialize("1", true, description: "The signal the item sends out when it's underwater.")]
|
||||
[InGameEditable, Serialize("1", true, description: "The signal the item sends out when it's underwater.", alwaysUseInstanceValues: true)]
|
||||
public string Output { get; set; }
|
||||
|
||||
[InGameEditable, Serialize("0", true, description: "The signal the item sends out when it's not underwater.")]
|
||||
[InGameEditable, Serialize("0", true, description: "The signal the item sends out when it's not underwater.", alwaysUseInstanceValues: true)]
|
||||
public string FalseOutput { get; set; }
|
||||
|
||||
public WaterDetector(Item item, XElement element)
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class WifiComponent : ItemComponent
|
||||
{
|
||||
private static List<WifiComponent> list = new List<WifiComponent>();
|
||||
private static readonly List<WifiComponent> list = new List<WifiComponent>();
|
||||
|
||||
private float range;
|
||||
|
||||
@@ -19,10 +19,10 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private string prevSignal;
|
||||
|
||||
[Serialize(Character.TeamType.None, true, description: "WiFi components can only communicate with components that have the same Team ID.")]
|
||||
[Serialize(Character.TeamType.None, true, description: "WiFi components can only communicate with components that have the same Team ID.", alwaysUseInstanceValues: true)]
|
||||
public Character.TeamType TeamID { get; set; }
|
||||
|
||||
[Editable, Serialize(20000.0f, false, description: "How close the recipient has to be to receive a signal from this WiFi component.")]
|
||||
[Editable, Serialize(20000.0f, false, description: "How close the recipient has to be to receive a signal from this WiFi component.", alwaysUseInstanceValues: true)]
|
||||
public float Range
|
||||
{
|
||||
get { return range; }
|
||||
@@ -35,7 +35,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[InGameEditable, Serialize(1, true, description: "WiFi components can only communicate with components that use the same channel.")]
|
||||
[InGameEditable, Serialize(1, true, description: "WiFi components can only communicate with components that use the same channel.", alwaysUseInstanceValues: true)]
|
||||
public int Channel
|
||||
{
|
||||
get { return channel; }
|
||||
@@ -46,7 +46,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
|
||||
[Serialize(false, false, description: "Can the component communicate with wifi components in another team's submarine (e.g. enemy sub in Combat missions, respawn shuttle). Needs to be enabled on both the component transmitting the signal and the component receiving it.")]
|
||||
[Editable, Serialize(false, true, description: "Can the component communicate with wifi components in another team's submarine (e.g. enemy sub in Combat missions, respawn shuttle). Needs to be enabled on both the component transmitting the signal and the component receiving it.", alwaysUseInstanceValues: true)]
|
||||
public bool AllowCrossTeamCommunication
|
||||
{
|
||||
get;
|
||||
@@ -54,7 +54,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, false, description: "If enabled, any signals received from another chat-linked wifi component are displayed " +
|
||||
"as chat messages in the chatbox of the player holding the item.")]
|
||||
"as chat messages in the chatbox of the player holding the item.", alwaysUseInstanceValues: true)]
|
||||
public bool LinkToChat
|
||||
{
|
||||
get;
|
||||
@@ -120,7 +120,7 @@ namespace Barotrauma.Items.Components
|
||||
public void TransmitSignal(int stepsTaken, string signal, Item source, Character sender, bool sendToChat, float signalStrength = 1.0f)
|
||||
{
|
||||
var senderComponent = source?.GetComponent<WifiComponent>();
|
||||
if (senderComponent != null && !CanReceive(senderComponent)) return;
|
||||
if (senderComponent != null && !CanReceive(senderComponent)) { return; }
|
||||
|
||||
bool chatMsgSent = false;
|
||||
|
||||
@@ -148,33 +148,32 @@ namespace Barotrauma.Items.Components
|
||||
if (LinkToChat && wifiComp.LinkToChat && chatMsgCooldown <= 0.0f && sendToChat)
|
||||
{
|
||||
if (wifiComp.item.ParentInventory != null &&
|
||||
wifiComp.item.ParentInventory.Owner != null &&
|
||||
GameMain.NetworkMember != null)
|
||||
wifiComp.item.ParentInventory.Owner != null)
|
||||
{
|
||||
string chatMsg = signal;
|
||||
if (senderComponent != null)
|
||||
{
|
||||
chatMsg = ChatMessage.ApplyDistanceEffect(chatMsg, 1.0f - sentSignalStrength);
|
||||
}
|
||||
if (chatMsg.Length > ChatMessage.MaxLength) chatMsg = chatMsg.Substring(0, ChatMessage.MaxLength);
|
||||
if (string.IsNullOrEmpty(chatMsg)) continue;
|
||||
if (chatMsg.Length > ChatMessage.MaxLength) { chatMsg = chatMsg.Substring(0, ChatMessage.MaxLength); }
|
||||
if (string.IsNullOrEmpty(chatMsg)) { continue; }
|
||||
|
||||
#if CLIENT
|
||||
if (wifiComp.item.ParentInventory.Owner == Character.Controlled)
|
||||
{
|
||||
if (GameMain.Client == null)
|
||||
GameMain.NetworkMember.AddChatMessage(signal, ChatMessageType.Radio, source == null ? "" : source.Name);
|
||||
{
|
||||
GameMain.GameSession?.CrewManager?.AddSinglePlayerChatMessage(source?.Name ?? "", signal, ChatMessageType.Radio, sender: null);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SERVER
|
||||
#elif SERVER
|
||||
if (GameMain.Server != null)
|
||||
{
|
||||
Client recipientClient = GameMain.Server.ConnectedClients.Find(c => c.Character == wifiComp.item.ParentInventory.Owner);
|
||||
if (recipientClient != null)
|
||||
{
|
||||
GameMain.Server.SendDirectChatMessage(
|
||||
ChatMessage.Create(source == null ? "" : source.Name, chatMsg, ChatMessageType.Radio, null), recipientClient);
|
||||
ChatMessage.Create(source?.Name ?? "", chatMsg, ChatMessageType.Radio, null), recipientClient);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -193,8 +192,20 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0.0f, float signalStrength = 1.0f)
|
||||
{
|
||||
if (connection == null || connection.Name != "signal_in") return;
|
||||
TransmitSignal(stepsTaken, signal, source, sender, true, signalStrength);
|
||||
if (connection == null) { return; }
|
||||
|
||||
switch (connection.Name)
|
||||
{
|
||||
case "signal_in":
|
||||
TransmitSignal(stepsTaken, signal, source, sender, true, signalStrength);
|
||||
break;
|
||||
case "set_channel":
|
||||
if (int.TryParse(signal, out int newChannel))
|
||||
{
|
||||
Channel = newChannel;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void RemoveComponentSpecific()
|
||||
|
||||
@@ -40,6 +40,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
private bool shouldClearConnections = true;
|
||||
|
||||
const float MaxAttachDistance = 150.0f;
|
||||
|
||||
const float MinNodeDistance = 7.0f;
|
||||
@@ -180,8 +182,6 @@ namespace Barotrauma.Items.Components
|
||||
newConnection.Item.Position :
|
||||
newConnection.Item.Position - refSub.HiddenSubPosition;
|
||||
|
||||
nodePos = RoundNode(nodePos);
|
||||
|
||||
if (nodes.Count > 0 && nodes[0] == nodePos) { break; }
|
||||
if (nodes.Count > 1 && nodes[nodes.Count - 1] == nodePos) { break; }
|
||||
|
||||
@@ -246,7 +246,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void Equip(Character character)
|
||||
{
|
||||
ClearConnections(character);
|
||||
if (shouldClearConnections) { ClearConnections(character); }
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
@@ -258,7 +258,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void Drop(Character dropper)
|
||||
{
|
||||
ClearConnections(dropper);
|
||||
if (shouldClearConnections) { ClearConnections(dropper); }
|
||||
IsActive = false;
|
||||
}
|
||||
|
||||
@@ -340,7 +340,7 @@ namespace Barotrauma.Items.Components
|
||||
else
|
||||
{
|
||||
#if CLIENT
|
||||
bool disableGrid = SubEditorScreen.IsSubEditor() && (PlayerInput.KeyDown(Keys.LeftShift) || PlayerInput.KeyDown(Keys.RightShift));
|
||||
bool disableGrid = SubEditorScreen.IsSubEditor() && PlayerInput.IsShiftDown();
|
||||
newNodePos = disableGrid ? item.Position : RoundNode(item.Position);
|
||||
#else
|
||||
newNodePos = RoundNode(item.Position);
|
||||
|
||||
@@ -4,7 +4,7 @@ using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using Barotrauma.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
@@ -40,6 +40,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private Character user;
|
||||
|
||||
private float resetUserTimer;
|
||||
|
||||
public float Rotation
|
||||
{
|
||||
get { return rotation; }
|
||||
@@ -102,7 +104,7 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(1, false, description: "How projectiles the weapon launches when fired once.")]
|
||||
[Serialize(1, false, description: "How many projectiles the weapon launches when fired once.")]
|
||||
public int ProjectileCount
|
||||
{
|
||||
get;
|
||||
@@ -116,7 +118,8 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize("0.0,0.0", true, description: "The range at which the barrel can rotate. TODO")]
|
||||
[Editable(VectorComponentLabels = new string[] { "editable.minvalue", "editable.maxvalue" }),
|
||||
Serialize("0.0,0.0", true, description: "The range at which the barrel can rotate.", alwaysUseInstanceValues: true)]
|
||||
public Vector2 RotationLimits
|
||||
{
|
||||
get
|
||||
@@ -196,7 +199,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
private float baseRotationRad;
|
||||
[Editable(0.0f, 360.0f), Serialize(0.0f, true, description: "The angle of the turret's base in degrees.")]
|
||||
[Editable(0.0f, 360.0f), Serialize(0.0f, true, description: "The angle of the turret's base in degrees.", alwaysUseInstanceValues: true)]
|
||||
public float BaseRotation
|
||||
{
|
||||
get { return MathHelper.ToDegrees(baseRotationRad); }
|
||||
@@ -285,6 +288,11 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
user = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
resetUserTimer -= deltaTime;
|
||||
if (resetUserTimer <= 0.0f) { user = null; }
|
||||
}
|
||||
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
|
||||
@@ -713,6 +721,7 @@ namespace Barotrauma.Items.Components
|
||||
PowerContainer batteryToLoad = null;
|
||||
foreach (PowerContainer battery in batteries)
|
||||
{
|
||||
if (battery.Item.NonInteractable) { continue; }
|
||||
if (batteryToLoad == null || battery.Charge < lowestCharge)
|
||||
{
|
||||
batteryToLoad = battery;
|
||||
@@ -733,20 +742,22 @@ namespace Barotrauma.Items.Components
|
||||
int maxProjectileCount = 0;
|
||||
foreach (MapEntity e in item.linkedTo)
|
||||
{
|
||||
if (!(e is Item projectileContainer)) continue;
|
||||
|
||||
var containedItems = projectileContainer.ContainedItems;
|
||||
if (containedItems != null)
|
||||
if (item.NonInteractable) { continue; }
|
||||
if (e is Item projectileContainer)
|
||||
{
|
||||
var container = projectileContainer.GetComponent<ItemContainer>();
|
||||
maxProjectileCount += container.Capacity;
|
||||
var containedItems = projectileContainer.ContainedItems;
|
||||
if (containedItems != null)
|
||||
{
|
||||
var container = projectileContainer.GetComponent<ItemContainer>();
|
||||
maxProjectileCount += container.Capacity;
|
||||
|
||||
int projectiles = containedItems.Count(it => it.Condition > 0.0f);
|
||||
usableProjectileCount += projectiles;
|
||||
int projectiles = containedItems.Count(it => it.Condition > 0.0f);
|
||||
usableProjectileCount += projectiles;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (usableProjectileCount == 0 || (usableProjectileCount < maxProjectileCount && objective.Option.Equals("fireatwill", StringComparison.OrdinalIgnoreCase)))
|
||||
if (usableProjectileCount == 0)
|
||||
{
|
||||
ItemContainer container = null;
|
||||
Item containerItem = null;
|
||||
@@ -754,11 +765,16 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
containerItem = e as Item;
|
||||
if (containerItem == null) { continue; }
|
||||
if (containerItem.NonInteractable) { continue; }
|
||||
if (character.AIController is HumanAIController aiController && aiController.IgnoredItems.Contains(containerItem)) { continue; }
|
||||
container = containerItem.GetComponent<ItemContainer>();
|
||||
if (container != null) { break; }
|
||||
}
|
||||
if (container == null || container.ContainableItems.Count == 0) { return true; }
|
||||
|
||||
if (container == null || container.ContainableItems.Count == 0)
|
||||
{
|
||||
character.Speak(TextManager.GetWithVariable("DialogCannotLoadTurret", "[itemname]", item.Name, true), null, 0.0f, "cannotloadturret", 30.0f);
|
||||
return true;
|
||||
}
|
||||
if (objective.SubObjectives.None())
|
||||
{
|
||||
if (!AIDecontainEmptyItems(character, objective, equip: true, sourceContainer: container))
|
||||
@@ -769,10 +785,25 @@ namespace Barotrauma.Items.Components
|
||||
if (objective.SubObjectives.None())
|
||||
{
|
||||
var loadItemsObjective = AIContainItems<Turret>(container, character, objective, usableProjectileCount + 1, equip: true, removeEmpty: true);
|
||||
loadItemsObjective.ignoredContainerIdentifiers = new string[] { containerItem.prefab.Identifier };
|
||||
character.Speak(TextManager.GetWithVariable("DialogLoadTurret", "[itemname]", item.Name, true), null, 0.0f, "loadturret", 30.0f);
|
||||
if (loadItemsObjective == null)
|
||||
{
|
||||
if (usableProjectileCount == 0)
|
||||
{
|
||||
character.Speak(TextManager.GetWithVariable("DialogCannotLoadTurret", "[itemname]", item.Name, true), null, 0.0f, "cannotloadturret", 30.0f);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
loadItemsObjective.ignoredContainerIdentifiers = new string[] { containerItem.prefab.Identifier };
|
||||
character.Speak(TextManager.GetWithVariable("DialogLoadTurret", "[itemname]", item.Name, true), null, 0.0f, "loadturret", 30.0f);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (objective.SubObjectives.Any())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//enough shells and power
|
||||
@@ -803,7 +834,10 @@ namespace Barotrauma.Items.Components
|
||||
character.AIController.SelectTarget(closestEnemy.AiTarget);
|
||||
|
||||
character.CursorPosition = closestEnemy.WorldPosition;
|
||||
if (item.Submarine != null) { character.CursorPosition -= item.Submarine.Position; }
|
||||
if (character.Submarine != null)
|
||||
{
|
||||
character.CursorPosition -= character.Submarine.Position;
|
||||
}
|
||||
|
||||
float enemyAngle = MathUtils.VectorToAngle(closestEnemy.WorldPosition - item.WorldPosition);
|
||||
float turretAngle = -rotation;
|
||||
@@ -857,11 +891,8 @@ namespace Barotrauma.Items.Components
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (objective.Option.Equals("fireatwill", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
character?.Speak(TextManager.GetWithVariable("DialogFireTurret", "[itemname]", item.Name, true), null, 0.0f, "fireturret", 5.0f);
|
||||
character.SetInput(InputType.Shoot, true, true);
|
||||
}
|
||||
character?.Speak(TextManager.GetWithVariable("DialogFireTurret", "[itemname]", item.Name, true), null, 0.0f, "fireturret", 5.0f);
|
||||
character.SetInput(InputType.Shoot, true, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -979,10 +1010,12 @@ namespace Barotrauma.Items.Components
|
||||
IsActive = true;
|
||||
}
|
||||
user = sender;
|
||||
resetUserTimer = 10.0f;
|
||||
break;
|
||||
case "trigger_in":
|
||||
item.Use((float)Timing.Step, sender);
|
||||
user = sender;
|
||||
resetUserTimer = 10.0f;
|
||||
//triggering the Use method through item.Use will fail if the item is not characterusable and the signal was sent by a character
|
||||
//so lets do it manually
|
||||
if (!characterUsable && sender != null)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Barotrauma.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Items.Components;
|
||||
@@ -221,11 +221,11 @@ namespace Barotrauma.Items.Components
|
||||
get { return variant; }
|
||||
set
|
||||
{
|
||||
#if SERVER
|
||||
if (variant == value) { return; }
|
||||
#if SERVER
|
||||
variant = value;
|
||||
item.CreateServerEvent(this);
|
||||
#elif CLIENT
|
||||
if (variant == value) { return; }
|
||||
|
||||
Character character = picker;
|
||||
if (character != null)
|
||||
@@ -370,11 +370,12 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void Unequip(Character character)
|
||||
{
|
||||
if (picker == null) return;
|
||||
if (character == null || character.Removed) { return; }
|
||||
if (picker == null) { return; }
|
||||
for (int i = 0; i < wearableSprites.Length; i++)
|
||||
{
|
||||
Limb equipLimb = character.AnimController.GetLimb(limbType[i]);
|
||||
if (equipLimb == null) continue;
|
||||
if (equipLimb == null) { continue; }
|
||||
|
||||
if (wearableSprites[i].LightComponent != null)
|
||||
{
|
||||
@@ -385,7 +386,6 @@ namespace Barotrauma.Items.Components
|
||||
#if CLIENT
|
||||
equipLimb.UpdateWearableTypesToHide();
|
||||
#endif
|
||||
|
||||
limb[i] = null;
|
||||
}
|
||||
|
||||
@@ -419,9 +419,14 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
base.RemoveComponentSpecific();
|
||||
|
||||
Unequip(picker);
|
||||
|
||||
foreach (WearableSprite wearableSprite in wearableSprites)
|
||||
{
|
||||
if (wearableSprite != null && wearableSprite.Sprite != null) wearableSprite.Sprite.Remove();
|
||||
if (wearableSprite != null && wearableSprite.Sprite != null)
|
||||
{
|
||||
wearableSprite.Sprite.Remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user