(965c31410a) Unstable v0.10.4.0

This commit is contained in:
Juan Pablo Arce
2020-07-21 08:57:50 -03:00
parent 4f8bd39789
commit 33d3a41104
546 changed files with 45952 additions and 25762 deletions
@@ -25,9 +25,6 @@ namespace Barotrauma.Items.Components
private readonly Hull[] hulls = new Hull[2];
private Gap gap;
private Door door;
private Body[] bodies;
private Fixture outsideBlocker;
private Body doorBody;
@@ -68,6 +65,8 @@ namespace Barotrauma.Items.Components
public DockingPort DockingTarget { get; private set; }
public Door Door { get; private set; }
public bool Docked
{
get
@@ -208,7 +207,7 @@ namespace Barotrauma.Items.Components
CreateJoint(false);
#if SERVER
if (GameMain.Server != null)
if (GameMain.Server != null && (!item.Submarine?.Loading ?? true))
{
item.CreateServerEvent(this);
}
@@ -250,7 +249,7 @@ namespace Barotrauma.Items.Components
CreateJoint(true);
#if SERVER
if (GameMain.Server != null)
if (GameMain.Server != null && (!item.Submarine?.Loading ?? true))
{
item.CreateServerEvent(this);
}
@@ -272,10 +271,10 @@ namespace Barotrauma.Items.Components
CreateHulls();
}
if (door != null && DockingTarget.door != null)
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);
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)
{
@@ -333,19 +332,19 @@ namespace Barotrauma.Items.Components
{
if (DockingDir != 0) { return DockingDir; }
if (door != null)
if (Door != null)
{
if (door.LinkedGap.linkedTo.Count == 1)
if (Door.LinkedGap.linkedTo.Count == 1)
{
return IsHorizontal ?
Math.Sign(door.Item.WorldPosition.X - door.LinkedGap.linkedTo[0].WorldPosition.X) :
Math.Sign(door.Item.WorldPosition.Y - door.LinkedGap.linkedTo[0].WorldPosition.Y);
Math.Sign(Door.Item.WorldPosition.X - Door.LinkedGap.linkedTo[0].WorldPosition.X) :
Math.Sign(Door.Item.WorldPosition.Y - Door.LinkedGap.linkedTo[0].WorldPosition.Y);
}
else if (dockingTarget?.door?.LinkedGap != null && dockingTarget.door.LinkedGap.linkedTo.Count == 1)
else if (dockingTarget?.Door?.LinkedGap != null && dockingTarget.Door.LinkedGap.linkedTo.Count == 1)
{
return IsHorizontal ?
Math.Sign(dockingTarget.door.LinkedGap.linkedTo[0].WorldPosition.X - dockingTarget.door.Item.WorldPosition.X) :
Math.Sign(dockingTarget.door.LinkedGap.linkedTo[0].WorldPosition.Y - dockingTarget.door.Item.WorldPosition.Y);
Math.Sign(dockingTarget.Door.LinkedGap.linkedTo[0].WorldPosition.X - dockingTarget.Door.Item.WorldPosition.X) :
Math.Sign(dockingTarget.Door.LinkedGap.linkedTo[0].WorldPosition.Y - dockingTarget.Door.Item.WorldPosition.Y);
}
}
if (dockingTarget != null)
@@ -367,24 +366,23 @@ namespace Barotrauma.Items.Components
private void ConnectWireBetweenPorts()
{
Wire wire = item.GetComponent<Wire>();
if (wire == null) return;
if (wire == null) { return; }
wire.Hidden = true;
wire.Locked = true;
wire.Hidden = true;
if (Item.Connections == null) return;
if (Item.Connections == null) { return; }
var powerConnection = Item.Connections.Find(c => c.IsPower);
if (powerConnection == null) return;
if (powerConnection == null) { return; }
if (DockingTarget == null || DockingTarget.item.Connections == null) return;
if (DockingTarget == null || DockingTarget.item.Connections == null) { return; }
var recipient = DockingTarget.item.Connections.Find(c => c.IsPower);
if (recipient == null) return;
if (recipient == null) { return; }
wire.RemoveConnection(item);
wire.RemoveConnection(DockingTarget.item);
powerConnection.TryAddLink(wire);
wire.Connect(powerConnection, false, false);
recipient.TryAddLink(wire);
@@ -399,13 +397,13 @@ namespace Barotrauma.Items.Components
doorBody = null;
}
Vector2 position = ConvertUnits.ToSimUnits(item.Position + (DockingTarget.door.Item.WorldPosition - item.WorldPosition));
Vector2 position = ConvertUnits.ToSimUnits(item.Position + (DockingTarget.Door.Item.WorldPosition - item.WorldPosition));
if (!MathUtils.IsValid(position))
{
string errorMsg =
"Attempted to create a door body at an invalid position (item pos: " + item.Position
+ ", item world pos: " + item.WorldPosition
+ ", docking target world pos: " + DockingTarget.door.Item.WorldPosition + ")\n" + Environment.StackTrace;
+ ", docking target world pos: " + DockingTarget.Door.Item.WorldPosition + ")\n" + Environment.StackTrace;
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce(
@@ -418,11 +416,11 @@ namespace Barotrauma.Items.Components
System.Diagnostics.Debug.Assert(doorBody == null);
doorBody = GameMain.World.CreateRectangle(
DockingTarget.door.Body.width,
DockingTarget.door.Body.height,
DockingTarget.Door.Body.width,
DockingTarget.Door.Body.height,
1.0f,
position);
doorBody.UserData = DockingTarget.door;
doorBody.UserData = DockingTarget.Door;
doorBody.CollisionCategories = Physics.CollisionWall;
doorBody.BodyType = BodyType.Static;
}
@@ -434,12 +432,12 @@ namespace Barotrauma.Items.Components
bodies = new Body[4];
if (DockingTarget.door != null)
if (DockingTarget.Door != null)
{
CreateDoorBody();
}
if (door != null)
if (Door != null)
{
DockingTarget.CreateDoorBody();
}
@@ -718,7 +716,7 @@ namespace Barotrauma.Items.Components
for (int i = 0; i < 2; i++)
{
Gap doorGap = i == 0 ? door?.LinkedGap : DockingTarget?.door?.LinkedGap;
Gap doorGap = i == 0 ? Door?.LinkedGap : DockingTarget?.Door?.LinkedGap;
if (doorGap == null) continue;
doorGap.DisableHullRechecks = true;
if (doorGap.linkedTo.Count >= 2) continue;
@@ -773,10 +771,10 @@ namespace Barotrauma.Items.Components
DockingTarget.item.Submarine.ConnectedDockingPorts.Remove(item.Submarine);
item.Submarine.ConnectedDockingPorts.Remove(DockingTarget.item.Submarine);
if (door != null && DockingTarget.door != null)
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);
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)
{
@@ -838,7 +836,7 @@ namespace Barotrauma.Items.Components
obstructedWayPointsDisabled = false;
#if SERVER
if (GameMain.Server != null)
if (GameMain.Server != null && (!item.Submarine?.Loading ?? true))
{
item.CreateServerEvent(this);
}
@@ -908,9 +906,9 @@ namespace Barotrauma.Items.Components
}
else
{
if (DockingTarget.door != null && doorBody != null)
if (DockingTarget.Door != null && doorBody != null)
{
doorBody.Enabled = DockingTarget.door.Body.Enabled;
doorBody.Enabled = DockingTarget.Door.Body.Enabled;
}
item.SendSignal(0, "1", "state_out", null);
@@ -927,6 +925,7 @@ namespace Barotrauma.Items.Components
protected override void RemoveComponentSpecific()
{
base.RemoveComponentSpecific();
list.Remove(this);
hulls[0]?.Remove(); hulls[0] = null;
hulls[1]?.Remove(); hulls[1] = null;
@@ -953,7 +952,7 @@ namespace Barotrauma.Items.Components
float distSqr = Vector2.DistanceSquared(item.Position, it.Position);
if (distSqr < closestDist)
{
door = doorComponent;
Door = doorComponent;
closestDist = distSqr;
}
}
@@ -982,7 +981,18 @@ namespace Barotrauma.Items.Components
{
InitializeLinks();
if (!item.linkedTo.Any()) return;
Wire wire = item.GetComponent<Wire>();
if (wire != null)
{
wire.Locked = true;
wire.Hidden = true;
if (wire.Connections.Contains(null))
{
wire.Drop(null);
}
}
if (!item.linkedTo.Any()) { return; }
List<MapEntity> linked = new List<MapEntity>(item.linkedTo);
foreach (MapEntity entity in linked)
@@ -995,6 +1005,7 @@ namespace Barotrauma.Items.Components
Dock(dockingPort);
}
}
}
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0.0f, float signalStrength = 1.0f)
@@ -78,7 +78,7 @@ namespace Barotrauma.Items.Components
private float RepairThreshold
{
get { return item.GetComponent<Repairable>() == null ? 0.0f : item.Prefab.Health; }
get { return item.GetComponent<Repairable>() == null ? 0.0f : item.MaxCondition; }
}
public bool CanBeWelded = true;
@@ -185,7 +185,10 @@ namespace Barotrauma.Items.Components
get;
set;
}
[Serialize(true, true, description: ""), Editable]
public bool UseBetweenOutpostModules { get; private set; }
public Door(Item item, XElement element)
: base(item, element)
{
@@ -419,8 +422,8 @@ namespace Barotrauma.Items.Components
linkedGap.Open = 1.0f;
IsOpen = false;
#if CLIENT
if (convexHull != null) convexHull.Enabled = false;
if (convexHull2 != null) convexHull2.Enabled = false;
if (convexHull != null) { convexHull.Enabled = false; }
if (convexHull2 != null) { convexHull2.Enabled = false; }
#endif
}
@@ -469,6 +472,14 @@ namespace Barotrauma.Items.Components
Body.Remove();
Body = null;
}
foreach (Gap gap in Gap.GapList)
{
if (gap.ConnectedDoor == this)
{
gap.ConnectedDoor = null;
}
}
//no need to remove the gap if we're unloading the whole submarine
//otherwise the gap will be removed twice and cause console warnings
@@ -485,13 +496,19 @@ namespace Barotrauma.Items.Components
#endif
}
bool itemPosErrorShown;
private readonly HashSet<Character> characterPosErrorShown = new HashSet<Character>();
private void PushCharactersAway()
{
if (!MathUtils.IsValid(item.SimPosition))
{
DebugConsole.ThrowError("Failed to push a character out of a doorway - position of the door is not valid (" + item.SimPosition + ")");
GameAnalyticsManager.AddErrorEventOnce("PushCharactersAway:DoorPosInvalid", GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Failed to push a character out of a doorway - position of the door is not valid (" + item.SimPosition + ").");
if (!itemPosErrorShown)
{
DebugConsole.ThrowError("Failed to push a character out of a doorway - position of the door is not valid (" + item.SimPosition + ")");
GameAnalyticsManager.AddErrorEventOnce("PushCharactersAway:DoorPosInvalid", GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Failed to push a character out of a doorway - position of the door is not valid (" + item.SimPosition + ").");
itemPosErrorShown = true;
}
return;
}
@@ -505,14 +522,18 @@ namespace Barotrauma.Items.Components
foreach (Character c in Character.CharacterList)
{
if (!c.Enabled) continue;
if (!c.Enabled) { continue; }
if (!MathUtils.IsValid(c.SimPosition))
{
if (GameSettings.VerboseLogging) { DebugConsole.ThrowError("Failed to push a character out of a doorway - position of the character \"" + c.Name + "\" is not valid (" + c.SimPosition + ")"); }
GameAnalyticsManager.AddErrorEventOnce("PushCharactersAway:CharacterPosInvalid", GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Failed to push a character out of a doorway - position of the character \"" + c.Name + "\" is not valid (" + c.SimPosition + ")." +
" Removed: " + c.Removed +
" Remoteplayer: " + c.IsRemotePlayer);
if (!characterPosErrorShown.Contains(c))
{
if (GameSettings.VerboseLogging) { DebugConsole.ThrowError("Failed to push a character out of a doorway - position of the character \"" + c.Name + "\" is not valid (" + c.SimPosition + ")"); }
GameAnalyticsManager.AddErrorEventOnce("PushCharactersAway:CharacterPosInvalid", GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Failed to push a character out of a doorway - position of the character \"" + c.Name + "\" is not valid (" + c.SimPosition + ")." +
" Removed: " + c.Removed +
" Remoteplayer: " + c.IsRemotePlayer);
characterPosErrorShown.Add(c);
}
continue;
}
int dir = IsHorizontal ? Math.Sign(c.SimPosition.Y - item.SimPosition.Y) : Math.Sign(c.SimPosition.X - item.SimPosition.X);
@@ -223,7 +223,6 @@ namespace Barotrauma.Items.Components
if (attachable)
{
prevMsg = DisplayMsg;
prevPickKey = PickKey;
prevRequiredItems = new Dictionary<RelatedItem.RelationType, List<RelatedItem>>(requiredItems);
}
}
@@ -254,9 +253,9 @@ namespace Barotrauma.Items.Components
if (item.body != null) { item.body.Enabled = true; }
IsActive = false;
if (picker == null)
if (picker == null || picker.Removed)
{
if (dropper == null) { return; }
if (dropper == null || dropper.Removed) { return; }
picker = dropper;
}
if (picker.Inventory == null) { return; }
@@ -285,7 +284,7 @@ namespace Barotrauma.Items.Components
heldHand = picker.AnimController.GetLimb(LimbType.RightHand);
arm = picker.AnimController.GetLimb(LimbType.RightArm);
}
if (heldHand != null && arm != null)
if (heldHand != null && !heldHand.Removed && arm != null && !arm.Removed)
{
//hand simPosition is actually in the wrist so need to move the item out from it slightly
Vector2 diff = new Vector2(
@@ -391,6 +390,8 @@ namespace Barotrauma.Items.Components
//allow deattaching everywhere in sub editor
if (Screen.Selected == GameMain.SubEditorScreen) { return true; }
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)))
@@ -581,7 +582,7 @@ namespace Barotrauma.Items.Components
}
Vector2 swing = Vector2.Zero;
if (swingAmount != Vector2.Zero)
if (swingAmount != Vector2.Zero && !picker.IsUnconscious && picker.Stun <= 0.0f)
{
swingState += deltaTime;
swingState %= 1.0f;
@@ -605,7 +606,7 @@ namespace Barotrauma.Items.Components
{
scaledHandlePos[0] = handlePos[0] * item.Scale;
scaledHandlePos[1] = handlePos[1] * item.Scale;
bool aim = picker.IsKeyDown(InputType.Aim) && aimPos != Vector2.Zero && (picker.SelectedConstruction == null || picker.SelectedConstruction.GetComponent<Ladder>() != null);
bool aim = picker.IsKeyDown(InputType.Aim) && aimPos != Vector2.Zero && picker.CanAim;
picker.AnimController.HoldItem(deltaTime, item, scaledHandlePos, holdPos + swing, aimPos + swing, aim, holdAngle);
}
else
@@ -678,17 +679,14 @@ namespace Barotrauma.Items.Components
}
var tempMsg = DisplayMsg;
var tempPickKey = PickKey;
var tempRequiredItems = requiredItems;
DisplayMsg = prevMsg;
PickKey = prevPickKey;
requiredItems = prevRequiredItems;
XElement saveElement = base.Save(parentElement);
DisplayMsg = tempMsg;
PickKey = tempPickKey;
requiredItems = tempRequiredItems;
return saveElement;
@@ -114,6 +114,7 @@ namespace Barotrauma.Items.Components
protected override void RemoveComponentSpecific()
{
base.RemoveComponentSpecific();
if (trigger != null)
{
trigger.Remove();
@@ -21,7 +21,7 @@ namespace Barotrauma.Items.Components
private float reloadTimer;
private readonly Attack attack;
public Attack Attack { get; private set; }
private readonly HashSet<Entity> hitTargets = new HashSet<Entity>();
@@ -56,7 +56,7 @@ namespace Barotrauma.Items.Components
foreach (XElement subElement in element.Elements())
{
if (!subElement.Name.ToString().Equals("attack", StringComparison.OrdinalIgnoreCase)) { continue; }
attack = new Attack(subElement, item.Name + ", MeleeWeapon");
Attack = new Attack(subElement, item.Name + ", MeleeWeapon");
}
item.IsShootable = true;
// TODO: should define this in xml if we have melee weapons that don't require aim to use
@@ -158,7 +158,7 @@ namespace Barotrauma.Items.Components
//TODO: refactor the hitting logic (get rid of the magic numbers, make it possible to use different kinds of animations for different items)
if (!hitting)
{
bool aim = picker.AllowInput && picker.IsKeyDown(InputType.Aim) && reloadTimer <= 0 && (picker.SelectedConstruction == null || picker.SelectedConstruction.GetComponent<Ladder>() != null);
bool aim = picker.AllowInput && picker.IsKeyDown(InputType.Aim) && reloadTimer <= 0 && picker.CanAim;
if (aim)
{
hitPos = MathUtils.WrapAnglePi(Math.Min(hitPos + deltaTime * 5f, MathHelper.PiOver4));
@@ -332,31 +332,31 @@ namespace Barotrauma.Items.Components
Structure targetStructure = target.UserData as Structure;
Item targetItem = target.UserData as Item;
if (attack != null)
if (Attack != null)
{
attack.SetUser(User);
Attack.SetUser(User);
if (targetLimb != null)
{
if (targetLimb.character.Removed) { return; }
targetLimb.character.LastDamageSource = item;
attack.DoDamageToLimb(User, targetLimb, item.WorldPosition, 1.0f);
Attack.DoDamageToLimb(User, targetLimb, item.WorldPosition, 1.0f);
}
else if (targetCharacter != null)
{
if (targetCharacter.Removed) { return; }
targetCharacter.LastDamageSource = item;
attack.DoDamage(User, targetCharacter, item.WorldPosition, 1.0f);
Attack.DoDamage(User, targetCharacter, item.WorldPosition, 1.0f);
}
else if (targetStructure != null)
{
if (targetStructure.Removed) { return; }
attack.DoDamage(User, targetStructure, item.WorldPosition, 1.0f);
Attack.DoDamage(User, targetStructure, item.WorldPosition, 1.0f);
}
else if (targetItem != null && targetItem.Prefab.DamagedByMeleeWeapons && targetItem.Condition > 0)
{
if (targetItem.Removed) { return; }
attack.DoDamage(User, targetItem, item.WorldPosition, 1.0f);
Attack.DoDamage(User, targetItem, item.WorldPosition, 1.0f);
}
else
{
@@ -25,7 +25,14 @@ namespace Barotrauma.Items.Components
public Character Picker
{
get { return picker; }
get
{
if (picker != null && picker.Removed)
{
picker = null;
}
return picker;
}
}
public Pickable(Item item, XElement element)
@@ -178,7 +178,7 @@ namespace Barotrauma.Items.Components
return true;
}
private Projectile FindProjectile(bool triggerOnUseOnContainers = false)
public Projectile FindProjectile(bool triggerOnUseOnContainers = false)
{
var containedItems = item.ContainedItems;
if (containedItems == null) { return null; }
@@ -604,19 +604,7 @@ namespace Barotrauma.Items.Components
}
if (item.RequireAimToUse)
{
bool isOperatingButtons = false;
if (character.AIController.SteeringManager is IndoorsSteeringManager indoorSteering)
{
var door = indoorSteering.CurrentPath?.CurrentNode?.ConnectedDoor;
if (door != null && !door.IsOpen)
{
isOperatingButtons = door.HasIntegratedButtons || door.Item.GetConnectedComponents<Controller>(true).Any();
}
}
if (!isOperatingButtons)
{
character.SetInput(InputType.Aim, false, true);
}
character.SetInput(InputType.Aim, false, true);
sinTime += deltaTime * 5;
}
// Press the trigger only when the tool is approximately facing the target.
@@ -74,7 +74,7 @@ namespace Barotrauma.Items.Components
if (picker.IsKeyDown(InputType.Aim) && picker.IsKeyHit(InputType.Shoot)) { throwing = true; }
if (!picker.IsKeyDown(InputType.Aim) && !throwing) { throwPos = 0.0f; }
bool aim = picker.IsKeyDown(InputType.Aim) && (picker.SelectedConstruction == null || picker.SelectedConstruction.GetComponent<Ladder>() != null);
bool aim = picker.IsKeyDown(InputType.Aim) && picker.CanAim;
if (picker.IsDead || !picker.AllowInput)
{
@@ -220,6 +220,8 @@ namespace Barotrauma.Items.Components
set;
}
public virtual bool RecreateGUIOnResolutionChange => false;
/// <summary>
/// How useful the item is in combat? Used by AI to decide which item it should use as a weapon. For the sake of clarity, use a value between 0 and 100 (not enforced).
/// </summary>
@@ -511,7 +513,11 @@ namespace Barotrauma.Items.Components
channel.Dispose();
}*/
if (GuiFrame != null) { GUI.RemoveFromUpdateList(GuiFrame, true); }
if (GuiFrame != null)
{
GUI.RemoveFromUpdateList(GuiFrame, true);
GuiFrame.RectTransform.Parent = null;
}
#endif
if (delayedCorrectionCoroutine != null)
@@ -545,8 +551,10 @@ namespace Barotrauma.Items.Components
RemoveComponentSpecific();
}
protected virtual void RemoveComponentSpecific()
{ }
{
}
public bool HasRequiredSkills(Character character)
{
@@ -558,7 +566,7 @@ namespace Barotrauma.Items.Components
foreach (Skill skill in requiredSkills)
{
float characterLevel = character.GetSkillLevel(skill.Identifier);
if (characterLevel < skill.Level)
if (characterLevel < skill.Level * GetSkillMultiplier())
{
insufficientSkill = skill;
return false;
@@ -568,6 +576,8 @@ namespace Barotrauma.Items.Components
return true;
}
public virtual float GetSkillMultiplier() { return 1; }
/// <summary>
/// Returns 0.0f-1.0f based on how well the Character can use the itemcomponent
/// </summary>
@@ -722,16 +732,27 @@ namespace Barotrauma.Items.Components
public void ApplyStatusEffects(ActionType type, float deltaTime, Character character = null, Limb targetLimb = null, Entity useTarget = null, Character user = null, Vector2? worldPosition = null)
{
if (statusEffectLists == null) return;
if (statusEffectLists == null) { return; }
if (!statusEffectLists.TryGetValue(type, out List<StatusEffect> statusEffects)) return;
if (!statusEffectLists.TryGetValue(type, out List<StatusEffect> statusEffects)) { return; }
bool broken = item.Condition <= 0.0f;
bool reducesCondition = false;
foreach (StatusEffect effect in statusEffects)
{
if (broken && effect.type != ActionType.OnBroken) { continue; }
if (user != null) { effect.SetUser(user); }
item.ApplyStatusEffect(effect, type, deltaTime, character, targetLimb, useTarget, false, false, worldPosition);
reducesCondition |= effect.ReducesItemCondition();
}
//if any of the effects reduce the item's condition, set the user for OnBroken effects as well
if (reducesCondition && user != null && type != ActionType.OnBroken)
{
foreach (ItemComponent ic in item.Components)
{
if (ic.statusEffectLists == null || !ic.statusEffectLists.TryGetValue(ActionType.OnBroken, out List<StatusEffect> brokenEffects)) { continue; }
brokenEffects.ForEach(e => e.SetUser(user));
}
}
}
@@ -935,12 +956,12 @@ namespace Barotrauma.Items.Components
return false;
}
protected AIObjectiveContainItem AIContainItems<T>(ItemContainer container, Character character, AIObjective objective, int itemCount, bool equip, bool removeEmpty) where T : ItemComponent
protected AIObjectiveContainItem AIContainItems<T>(ItemContainer container, Character character, AIObjective objective, int itemCount, bool equip, bool removeEmpty, bool spawnItemIfNotFound = false) where T : ItemComponent
{
AIObjectiveContainItem containObjective = null;
if (character.AIController is HumanAIController aiController)
{
containObjective = new AIObjectiveContainItem(character, container.GetContainableItemIdentifiers.ToArray(), container, objective.objectiveManager)
containObjective = new AIObjectiveContainItem(character, container.GetContainableItemIdentifiers.ToArray(), container, objective.objectiveManager, spawnItemIfNotFound: spawnItemIfNotFound)
{
targetItemCount = itemCount,
Equip = equip,
@@ -107,6 +107,8 @@ namespace Barotrauma.Items.Components
public IEnumerable<string> GetContainableItemIdentifiers => ContainableItems.SelectMany(ri => ri.Identifiers);
public override bool RecreateGUIOnResolutionChange => true;
public ItemContainer(Item item, XElement element)
: base (item, element)
{
@@ -352,18 +354,12 @@ namespace Barotrauma.Items.Components
public override void OnMapLoaded()
{
if (itemIds == null) return;
if (itemIds == null) { return; }
for (ushort i = 0; i < itemIds.Length; i++)
{
Item item = Entity.FindEntityByID(itemIds[i]) as Item;
if (item == null) continue;
if (i >= Inventory.Capacity)
{
continue;
}
if (!(Entity.FindEntityByID(itemIds[i]) is Item item)) { continue; }
if (i >= Inventory.Capacity) { continue; }
Inventory.TryPutItem(item, i, false, false, null, false);
}
@@ -376,6 +372,7 @@ namespace Barotrauma.Items.Components
protected override void RemoveComponentSpecific()
{
base.RemoveComponentSpecific();
#if CLIENT
inventoryTopSprite?.Remove();
inventoryBackSprite?.Remove();
@@ -26,6 +26,7 @@ namespace Barotrauma.Items.Components
protected override void RemoveComponentSpecific()
{
base.RemoveComponentSpecific();
RemoveProjSpecific();
List.Remove(this);
}
@@ -9,15 +9,24 @@ using Barotrauma.Extensions;
namespace Barotrauma.Items.Components
{
struct LimbPos
class LimbPos : ISerializableEntity
{
public LimbType limbType;
public Vector2 position;
[Editable]
public LimbType LimbType { get; set; }
[Editable]
public Vector2 Position { get; set; }
public LimbPos(LimbType limbType, Vector2 position)
public bool AllowUsingLimb;
public string Name => LimbType.ToString();
public Dictionary<string, SerializableProperty> SerializableProperties => null;
public LimbPos(LimbType limbType, Vector2 position, bool allowUsingLimb)
{
this.limbType = limbType;
this.position = position;
LimbType = limbType;
Position = position;
AllowUsingLimb = allowUsingLimb;
}
}
@@ -66,11 +75,32 @@ namespace Barotrauma.Items.Components
set;
}
[Serialize(true, false, description: "Should the HUD (inventory, health bar, etc) be hidden when this item is selected.")]
public bool HideHUD
{
get;
set;
}
public enum UseEnvironment
{
Air, Water, Both
};
[Serialize(UseEnvironment.Both, false, description: "Can the item be selected in air, underwater or both.")]
public UseEnvironment UsableIn { get; set; }
public bool ControlCharacterPose
{
get { return limbPositions.Count > 0; }
}
public bool AllowAiming
{
get;
private set;
} = true;
public Controller(Item item, XElement element)
: base(item, element)
{
@@ -79,25 +109,31 @@ namespace Barotrauma.Items.Components
userPos = element.GetAttributeVector2("UserPos", Vector2.Zero);
Enum.TryParse(element.GetAttributeString("direction", "None"), out dir);
foreach (XElement el in element.Elements())
foreach (XElement subElement in element.Elements())
{
if (el.Name != "limbposition") continue;
LimbPos lp = new LimbPos();
try
if (subElement.Name != "limbposition") { continue; }
string limbStr = subElement.GetAttributeString("limb", "");
if (!Enum.TryParse(subElement.Attribute("limb").Value, out LimbType limbType))
{
lp.limbType = (LimbType)Enum.Parse(typeof(LimbType), el.Attribute("limb").Value, true);
DebugConsole.ThrowError($"Error in item \"{item.Name}\" - {limbStr} is not a valid limb type.");
}
catch (Exception e)
else
{
DebugConsole.ThrowError("Error in " + element + ": " + e.Message, e);
LimbPos limbPos = new LimbPos(limbType,
subElement.GetAttributeVector2("position", Vector2.Zero),
subElement.GetAttributeBool("allowusinglimb", false));
limbPositions.Add(limbPos);
if (!limbPos.AllowUsingLimb)
{
if (limbType == LimbType.RightHand || limbType == LimbType.RightForearm || limbType == LimbType.RightArm ||
limbType == LimbType.LeftHand || limbType == LimbType.LeftForearm || limbType == LimbType.LeftArm)
{
AllowAiming = false;
}
}
}
lp.position = el.GetAttributeVector2("position", Vector2.Zero);
limbPositions.Add(lp);
}
IsActive = true;
@@ -115,7 +151,9 @@ namespace Barotrauma.Items.Components
if (user == null
|| user.Removed
|| user.SelectedConstruction != item
|| !user.CanInteractWith(item))
|| !user.CanInteractWith(item)
|| (UsableIn == UseEnvironment.Water && !user.AnimController.InWater)
|| (UsableIn == UseEnvironment.Air && user.AnimController.InWater))
{
if (user != null)
{
@@ -187,12 +225,29 @@ namespace Barotrauma.Items.Components
foreach (LimbPos lb in limbPositions)
{
Limb limb = user.AnimController.GetLimb(lb.limbType);
if (limb == null || !limb.body.Enabled) continue;
Limb limb = user.AnimController.GetLimb(lb.LimbType);
if (limb == null || !limb.body.Enabled) { continue; }
if (lb.AllowUsingLimb)
{
switch (lb.LimbType)
{
case LimbType.RightHand:
case LimbType.RightForearm:
case LimbType.RightArm:
if (user.SelectedItems[0] != null) { continue; }
break;
case LimbType.LeftHand:
case LimbType.LeftForearm:
case LimbType.LeftArm:
if ( user.SelectedItems[1] != null) { continue; }
break;
}
}
limb.Disabled = true;
Vector2 worldPosition = new Vector2(item.WorldRect.X, item.WorldRect.Y) + lb.position * item.Scale;
Vector2 worldPosition = new Vector2(item.WorldRect.X, item.WorldRect.Y) + lb.Position * item.Scale;
Vector2 diff = worldPosition - limb.WorldPosition;
limb.PullJointEnabled = true;
@@ -255,7 +310,7 @@ namespace Barotrauma.Items.Components
Lights.LightManager.ViewTarget = focusTarget;
cam.TargetPos = focusTarget.WorldPosition;
cam.OffsetAmount = MathHelper.Lerp(cam.OffsetAmount, (focusTarget as Item).Prefab.OffsetOnSelected, deltaTime * 10.0f);
cam.OffsetAmount = MathHelper.Lerp(cam.OffsetAmount, (focusTarget as Item).Prefab.OffsetOnSelected * focusTarget.OffsetOnSelectedMultiplier, deltaTime * 10.0f);
HideHUDs(true);
}
#endif
@@ -326,8 +381,8 @@ namespace Barotrauma.Items.Components
foreach (LimbPos lb in limbPositions)
{
Limb limb = character.AnimController.GetLimb(lb.limbType);
if (limb == null) continue;
Limb limb = character.AnimController.GetLimb(lb.LimbType);
if (limb == null) { continue; }
limb.Disabled = false;
limb.PullJointEnabled = false;
@@ -349,6 +404,12 @@ namespace Barotrauma.Items.Components
{
if (activator == null || activator.Removed) { return false; }
if (UsableIn == UseEnvironment.Water && !activator.AnimController.InWater ||
UsableIn == UseEnvironment.Air && activator.AnimController.InWater)
{
return false;
}
//someone already using the item
if (user != null && !user.Removed)
{
@@ -383,14 +444,13 @@ namespace Barotrauma.Items.Components
for (int i = 0; i < limbPositions.Count; i++)
{
float diff = (item.Rect.X + limbPositions[i].position.X * item.Scale) - item.Rect.Center.X;
float diff = (item.Rect.X + limbPositions[i].Position.X * item.Scale) - item.Rect.Center.X;
Vector2 flippedPos =
new Vector2(
(item.Rect.Center.X - diff - item.Rect.X) / item.Scale,
limbPositions[i].position.Y);
limbPositions[i] = new LimbPos(limbPositions[i].limbType, flippedPos);
limbPositions[i].Position.Y);
limbPositions[i] = new LimbPos(limbPositions[i].LimbType, flippedPos, limbPositions[i].AllowUsingLimb);
}
}
@@ -400,14 +460,13 @@ namespace Barotrauma.Items.Components
for (int i = 0; i < limbPositions.Count; i++)
{
float diff = (item.Rect.Y + limbPositions[i].position.Y) - item.Rect.Center.Y;
float diff = (item.Rect.Y + limbPositions[i].Position.Y) - item.Rect.Center.Y;
Vector2 flippedPos =
new Vector2(
limbPositions[i].position.X,
limbPositions[i].Position.X,
item.Rect.Center.Y - diff - item.Rect.Y);
limbPositions[i] = new LimbPos(limbPositions[i].limbType, flippedPos);
limbPositions[i] = new LimbPos(limbPositions[i].LimbType, flippedPos, limbPositions[i].AllowUsingLimb);
}
}
@@ -23,6 +23,11 @@ namespace Barotrauma.Items.Components
{
get { return outputContainer; }
}
[Editable, Serialize(1.0f, true)]
public float DeconstructionSpeed { get; set; }
public override bool RecreateGUIOnResolutionChange => true;
public Deconstructor(Item item, XElement element)
: base(item, element)
@@ -77,7 +82,7 @@ namespace Barotrauma.Items.Components
var targetItem = inputContainer.Inventory.Items.LastOrDefault(i => i != null);
if (targetItem == null) { return; }
float deconstructTime = targetItem.Prefab.DeconstructItems.Any() ? targetItem.Prefab.DeconstructTime : 1.0f;
float deconstructTime = targetItem.Prefab.DeconstructItems.Any() ? targetItem.Prefab.DeconstructTime / DeconstructionSpeed : 1.0f;
progressState = Math.Min(progressTimer / deconstructTime, 1.0f);
if (progressTimer > deconstructTime)
@@ -7,7 +7,6 @@ using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
partial class Fabricator : Powered, IServerSerializable, IClientSerializable
{
private readonly List<FabricationRecipe> fabricationRecipes = new List<FabricationRecipe>();
@@ -21,6 +20,12 @@ namespace Barotrauma.Items.Components
private Character user;
private ItemContainer inputContainer, outputContainer;
[Serialize(1.0f, true)]
public float FabricationSpeed { get; set; }
[Serialize(1.0f, true)]
public float SkillRequirementMultiplier { get; set; }
private enum FabricatorState
{
@@ -56,6 +61,8 @@ namespace Barotrauma.Items.Components
get { return outputContainer; }
}
public override bool RecreateGUIOnResolutionChange => true;
private float progressState;
public Fabricator(Item item, XElement element)
@@ -287,13 +294,27 @@ namespace Barotrauma.Items.Components
}
}
Character tempUser = user;
if (outputContainer.Inventory.Items.All(i => i != null))
{
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, item.Position, item.Submarine, fabricatedItem.TargetItem.Health * fabricatedItem.OutCondition);
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, item.Position, item.Submarine, fabricatedItem.TargetItem.Health * fabricatedItem.OutCondition,
onSpawned: (Item spawnedItem) => { onItemSpawned(spawnedItem, tempUser); });
}
else
{
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, outputContainer.Inventory, fabricatedItem.TargetItem.Health * fabricatedItem.OutCondition);
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, outputContainer.Inventory, fabricatedItem.TargetItem.Health * fabricatedItem.OutCondition,
onSpawned: (Item spawnedItem) => { onItemSpawned(spawnedItem, tempUser); });
}
static void onItemSpawned(Item spawnedItem, Character user)
{
if (user != null && user.TeamID != Character.TeamType.None)
{
foreach (WifiComponent wifiComponent in spawnedItem.GetComponents<WifiComponent>())
{
wifiComponent.TeamID = user.TeamID;
}
}
}
if (user != null && !user.Removed)
@@ -334,13 +355,28 @@ namespace Barotrauma.Items.Components
private float GetRequiredTime(FabricationRecipe fabricableItem, Character user)
{
float degreeOfSuccess = DegreeOfSuccess(user, fabricableItem.RequiredSkills);
float degreeOfSuccess = FabricationDegreeOfSuccess(user, fabricableItem.RequiredSkills);
float t = degreeOfSuccess < 0.5f ? degreeOfSuccess * degreeOfSuccess : degreeOfSuccess * 2;
//fabricating takes 100 times longer if degree of success is close to 0
//characters with a higher skill than required can fabricate up to 100% faster
return fabricableItem.RequiredTime / MathHelper.Clamp(t, 0.01f, 2.0f);
return fabricableItem.RequiredTime / FabricationSpeed / MathHelper.Clamp(t, 0.01f, 2.0f);
}
public float FabricationDegreeOfSuccess(Character character, List<Skill> skills)
{
if (skills.Count == 0) return 1.0f;
float skillSum = (from t in skills let characterLevel = character.GetSkillLevel(t.Identifier) select (characterLevel - (t.Level * SkillRequirementMultiplier))).Sum();
float average = skillSum / skills.Count;
return ((average + 100.0f) / 2.0f) / 100.0f;
}
public override float GetSkillMultiplier()
{
return SkillRequirementMultiplier;
}
/// <summary>
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
partial class OutpostTerminal : ItemComponent
{
public OutpostTerminal(Item item, XElement element) : base(item, element)
{
InitProjSpecific(element);
}
partial void InitProjSpecific(XElement element);
}
}
@@ -34,6 +34,13 @@ namespace Barotrauma.Items.Components
set { maxFlow = value; }
}
[Editable, Serialize(false, false, alwaysUseInstanceValues: true)]
public bool IsOn
{
get { return IsActive; }
set { IsActive = value; }
}
private float currFlow;
public float CurrFlow
{
@@ -172,6 +172,8 @@ namespace Barotrauma.Items.Components
}
private float prevAvailableFuel;
[Serialize(0.0f, true)]
public float AvailableFuel { get; set; }
public Reactor(Item item, XElement element)
@@ -316,9 +318,12 @@ namespace Barotrauma.Items.Components
if (item.CurrentHull != null)
{
var aiTarget = item.CurrentHull.AiTarget;
float range = Math.Abs(currPowerConsumption) / MaxPowerOutput;
float noise = MathHelper.Lerp(aiTarget.MinSoundRange, aiTarget.MaxSoundRange, range);
aiTarget.SoundRange = Math.Max(aiTarget.SoundRange, noise);
if (aiTarget != null)
{
float range = Math.Abs(currPowerConsumption) / MaxPowerOutput;
float noise = MathHelper.Lerp(aiTarget.MinSoundRange, aiTarget.MaxSoundRange, range);
aiTarget.SoundRange = Math.Max(aiTarget.SoundRange, noise);
}
}
if (item.AiTarget != null)
@@ -447,7 +452,7 @@ namespace Barotrauma.Items.Components
}
}
private void UpdateAutoTemp(float speed, float deltaTime)
public void UpdateAutoTemp(float speed, float deltaTime)
{
float desiredTurbineOutput = (optimalTurbineOutput.X + optimalTurbineOutput.Y) / 2.0f;
targetTurbineOutput += MathHelper.Clamp(desiredTurbineOutput - targetTurbineOutput, -speed, speed) * deltaTime;
@@ -471,6 +476,19 @@ namespace Barotrauma.Items.Components
//for the actual fission rate and temperature to follow
targetFissionRate = MathHelper.Clamp(targetFissionRate, FissionRate - 5, FissionRate + 5);
}
public void PowerUpImmediately()
{
PowerOn = true;
AutoTemp = true;
prevAvailableFuel = AvailableFuel;
for (int i = 0; i < 100; i++)
{
Update((float)(Timing.Step * 10.0f), cam: null);
UpdateAutoTemp(100.0f, (float)(Timing.Step * 10.0f));
AvailableFuel = prevAvailableFuel;
}
}
public override void UpdateBroken(float deltaTime, Camera cam)
{
@@ -557,7 +575,7 @@ namespace Barotrauma.Items.Components
if (objective.SubObjectives.None())
{
int itemCount = item.ContainedItems.Count(i => i != null && container.ContainableItems.Any(ri => ri.MatchesItem(i))) + 1;
AIContainItems<Reactor>(container, character, objective, itemCount, equip: false, removeEmpty: true);
AIContainItems<Reactor>(container, character, objective, itemCount, equip: false, removeEmpty: true, spawnItemIfNotFound: character.TeamID == Character.TeamType.FriendlyNPC);
character.Speak(TextManager.Get("DialogReactorFuel"), null, 0.0f, "reactorfuel", 30.0f);
}
return false;
@@ -641,6 +659,11 @@ namespace Barotrauma.Items.Components
return false;
}
public override void OnMapLoaded()
{
prevAvailableFuel = AvailableFuel;
}
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power, float signalStrength = 1.0f)
{
switch (connection.Name)
@@ -130,6 +130,8 @@ namespace Barotrauma.Items.Components
}
}
public override bool RecreateGUIOnResolutionChange => true;
public Sonar(Item item, XElement element)
: base(item, element)
{
@@ -230,7 +232,7 @@ namespace Barotrauma.Items.Components
public override bool Use(float deltaTime, Character character = null)
{
return currentPingIndex != -1;
return currentPingIndex != -1 && (character == null || characterUsable);
}
private static readonly Dictionary<string, List<Character>> targetGroups = new Dictionary<string, List<Character>>();
@@ -146,6 +146,8 @@ namespace Barotrauma.Items.Components
set { posToMaintain = value; }
}
public override bool RecreateGUIOnResolutionChange => true;
struct ObstacleDebugInfo
{
public Vector2 Point1;
@@ -330,6 +332,8 @@ namespace Barotrauma.Items.Components
private void IncreaseSkillLevel(Character user, float deltaTime)
{
if (user?.Info == null) { return; }
// Do not increase the helm skill when "steering" the sub in an outpost level
if (GameMain.GameSession?.Campaign != null && Level.IsLoadedOutpost) { return; }
float userSkill = user.GetSkillLevel("helm") / 100.0f;
user.Info.IncreaseSkillLevel(
@@ -21,9 +21,12 @@ namespace Barotrauma.Items.Components
public override void Update(float deltaTime, Camera cam)
{
if (item.CurrentHull == null) return;
if (item.CurrentHull == null || item.InWater) { return; }
if (item.InWater) return;
if (oxygenFlow > 0.0f)
{
ApplyStatusEffects(ActionType.OnActive, deltaTime);
}
item.CurrentHull.Oxygen += oxygenFlow * deltaTime;
OxygenFlow -= deltaTime * 1000.0f;
@@ -79,7 +79,7 @@ namespace Barotrauma.Items.Components
if (Math.Abs(charge - lastSentCharge) / capacity > 0.05f)
{
#if SERVER
if (GameMain.Server != null) item.CreateServerEvent(this);
if (GameMain.Server != null && (!item.Submarine?.Loading ?? true)) { item.CreateServerEvent(this); }
#endif
lastSentCharge = charge;
}
@@ -17,6 +17,10 @@ namespace Barotrauma.Items.Components
/// List of all powered ItemComponents
/// </summary>
private static readonly List<Powered> poweredList = new List<Powered>();
public static IEnumerable<Powered> PoweredList
{
get { return poweredList; }
}
/// <summary>
/// Items that have already received the "probe signal" that's used to distribute power and load across the grid
@@ -306,6 +310,7 @@ namespace Barotrauma.Items.Components
protected override void RemoveComponentSpecific()
{
base.RemoveComponentSpecific();
poweredList.Remove(this);
}
}
@@ -8,6 +8,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Voronoi2;
namespace Barotrauma.Items.Components
{
@@ -51,7 +52,7 @@ namespace Barotrauma.Items.Components
private const float PersistentStickJointDuration = 1.0f;
private PrismaticJoint stickJoint;
private readonly Attack attack;
public Attack Attack { get; private set; }
private Vector2 launchPos;
@@ -66,7 +67,7 @@ namespace Barotrauma.Items.Components
set
{
user = value;
attack?.SetUser(user);
Attack?.SetUser(user);
}
}
@@ -186,27 +187,27 @@ namespace Barotrauma.Items.Components
foreach (XElement subElement in element.Elements())
{
if (!subElement.Name.ToString().Equals("attack", StringComparison.OrdinalIgnoreCase)) { continue; }
attack = new Attack(subElement, item.Name + ", Projectile");
Attack = new Attack(subElement, item.Name + ", Projectile");
}
}
public override void OnItemLoaded()
{
if (attack != null && attack.DamageRange <= 0.0f && item.body != null)
if (Attack != null && Attack.DamageRange <= 0.0f && item.body != null)
{
switch (item.body.BodyShape)
{
case PhysicsBody.Shape.Circle:
attack.DamageRange = item.body.radius;
Attack.DamageRange = item.body.radius;
break;
case PhysicsBody.Shape.Capsule:
attack.DamageRange = item.body.height / 2 + item.body.radius;
Attack.DamageRange = item.body.height / 2 + item.body.radius;
break;
case PhysicsBody.Shape.Rectangle:
attack.DamageRange = new Vector2(item.body.width / 2.0f, item.body.height / 2.0f).Length();
Attack.DamageRange = new Vector2(item.body.width / 2.0f, item.body.height / 2.0f).Length();
break;
}
attack.DamageRange = ConvertUnits.ToDisplayUnits(attack.DamageRange);
Attack.DamageRange = ConvertUnits.ToDisplayUnits(Attack.DamageRange);
}
}
@@ -253,7 +254,7 @@ namespace Barotrauma.Items.Components
launchPos = item.SimPosition;
item.body.Enabled = true;
item.body.ApplyLinearImpulse(impulse, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
item.body.ApplyLinearImpulse(impulse, maxVelocity: NetConfig.MaxPhysicsBodyVelocity * 0.9f);
item.body.FarseerBody.OnCollision += OnProjectileCollision;
item.body.FarseerBody.IsBullet = true;
@@ -367,6 +368,8 @@ namespace Barotrauma.Items.Components
!fixture.CollisionCategories.HasFlag(Physics.CollisionWall) &&
!fixture.CollisionCategories.HasFlag(Physics.CollisionLevel)) { return true; }
if (fixture.Body.UserData is VoronoiCell && this.item.Submarine != null) { return true; }
fixture.Body.GetTransform(out FarseerPhysics.Common.Transform transform);
if (!fixture.Shape.TestPoint(ref transform, ref rayStart)) { return true; }
@@ -387,6 +390,15 @@ namespace Barotrauma.Items.Components
!fixture.CollisionCategories.HasFlag(Physics.CollisionWall) &&
!fixture.CollisionCategories.HasFlag(Physics.CollisionLevel)) { return -1; }
//ignore level cells if the item the point of impact are inside a sub
if (fixture.Body.UserData is VoronoiCell && this.item.Submarine != null)
{
if (Hull.FindHull(ConvertUnits.ToDisplayUnits(point), this.item.CurrentHull) != null)
{
return -1;
}
}
hits.Add(new HitscanResult(fixture, point, normal, fraction));
return hits.Count < 25 ? 1 : 0;
@@ -532,25 +544,24 @@ namespace Barotrauma.Items.Components
else if (target.Body.UserData is Limb limb)
{
//severed limbs don't deactivate the projectile (but may still slow it down enough to make it inactive)
if (limb.IsSevered)
{
return true;
}
if (limb.IsSevered) { return true; }
if (limb.character == null || limb.character.Removed) { return false; }
limb.character.LastDamageSource = item;
if (attack != null) { attackResult = attack.DoDamageToLimb(User, limb, item.WorldPosition, 1.0f); }
if (Attack != null) { attackResult = Attack.DoDamageToLimb(User, limb, item.WorldPosition, 1.0f); }
if (limb.character != null) { character = limb.character; }
}
else if (target.Body.UserData is Item targetItem)
{
if (attack != null && targetItem.Prefab.DamagedByProjectiles && targetItem.Condition > 0)
if (targetItem.Removed) { return false; }
if (Attack != null && targetItem.Prefab.DamagedByProjectiles && targetItem.Condition > 0)
{
attackResult = attack.DoDamage(User, targetItem, item.WorldPosition, 1.0f);
attackResult = Attack.DoDamage(User, targetItem, item.WorldPosition, 1.0f);
}
}
else if (target.Body.UserData is IDamageable damageable)
{
if (attack != null) { attackResult = attack.DoDamage(User, damageable, item.WorldPosition, 1.0f); }
if (Attack != null) { attackResult = Attack.DoDamage(User, damageable, item.WorldPosition, 1.0f); }
}
if (character != null) { character.LastDamageSource = item; }
@@ -738,6 +749,7 @@ namespace Barotrauma.Items.Components
protected override void RemoveComponentSpecific()
{
base.RemoveComponentSpecific();
if (stickJoint != null)
{
try
@@ -2,6 +2,7 @@
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Xml.Linq;
@@ -83,6 +84,30 @@ namespace Barotrauma.Items.Components
set;
}
private float skillRequirementMultiplier;
[Serialize(1.0f, true)]
public float SkillRequirementMultiplier
{
get { return skillRequirementMultiplier; }
set
{
var oldValue = skillRequirementMultiplier;
skillRequirementMultiplier = value;
#if CLIENT
if (!MathUtils.NearlyEqual(oldValue, skillRequirementMultiplier))
{
RecreateGUI();
}
#endif
}
}
public float RepairIconThreshold
{
get { return RepairThreshold / 2; }
}
public Character CurrentFixer { get; private set; }
public enum FixActions : int
@@ -146,12 +171,27 @@ namespace Barotrauma.Items.Components
if (requiredSkills.Any(s => s != null && s.Identifier.Equals("electrical", StringComparison.OrdinalIgnoreCase)) &&
item.GetComponent<Powered>() is Powered powered && powered.Voltage < 0.1f) { return true; }
if (Rand.Range(0.0f, 0.5f) < DegreeOfSuccess(character)) { return true; }
if (Rand.Range(0.0f, 0.5f) < RepairDegreeOfSuccess(character, requiredSkills)) { return true; }
ApplyStatusEffects(ActionType.OnFailure, 1.0f, character);
return false;
}
public override float GetSkillMultiplier()
{
return SkillRequirementMultiplier;
}
public float RepairDegreeOfSuccess(Character character, List<Skill> skills)
{
if (skills.Count == 0) return 1.0f;
float skillSum = (from t in skills let characterLevel = character.GetSkillLevel(t.Identifier) select (characterLevel - (t.Level * SkillRequirementMultiplier))).Sum();
float average = skillSum / skills.Count;
return ((average + 100.0f) / 2.0f) / 100.0f;
}
public bool StartRepairing(Character character, FixActions action)
{
if (character == null || character.IsDead || action == FixActions.None)
@@ -213,7 +253,7 @@ namespace Barotrauma.Items.Components
public void ResetDeterioration()
{
deteriorationTimer = Rand.Range(MinDeteriorationDelay, MaxDeteriorationDelay);
item.Condition = item.Prefab.Health;
item.Condition = item.MaxCondition;
#if SERVER
//let the clients know the deterioration delay
item.CreateServerEvent(this);
@@ -271,7 +311,7 @@ namespace Barotrauma.Items.Components
return;
}
float successFactor = requiredSkills.Count == 0 ? 1.0f : DegreeOfSuccess(CurrentFixer, requiredSkills);
float successFactor = requiredSkills.Count == 0 ? 1.0f : RepairDegreeOfSuccess(CurrentFixer, requiredSkills);
//item must have been below the repair threshold for the player to get an achievement or XP for repairing it
if (item.ConditionPercentage < RepairThreshold)
@@ -368,6 +408,8 @@ namespace Barotrauma.Items.Components
private bool ShouldDeteriorate()
{
if (Level.IsLoadedOutpost) { return false; }
if (LastActiveTime > Timing.TotalTime) { return true; }
foreach (ItemComponent ic in item.Components)
{
@@ -67,10 +67,10 @@ namespace Barotrauma.Items.Components
#if CLIENT
if (connector == null)
{
connector = GUI.Style.GetComponentStyle("ConnectionPanelConnector").Sprites[GUIComponent.ComponentState.None][0].Sprite;
wireVertical = GUI.Style.GetComponentStyle("ConnectionPanelWire").Sprites[GUIComponent.ComponentState.None][0].Sprite;
connectionSprite = GUI.Style.GetComponentStyle("ConnectionPanelConnection").Sprites[GUIComponent.ComponentState.None][0].Sprite;
connectionSpriteHighlight = GUI.Style.GetComponentStyle("ConnectionPanelConnection").Sprites[GUIComponent.ComponentState.Hover][0].Sprite;
connector = GUI.Style.GetComponentStyle("ConnectionPanelConnector").GetDefaultSprite();
wireVertical = GUI.Style.GetComponentStyle("ConnectionPanelWire").GetDefaultSprite();
connectionSprite = GUI.Style.GetComponentStyle("ConnectionPanelConnection").GetDefaultSprite();
connectionSpriteHighlight = GUI.Style.GetComponentStyle("ConnectionPanelConnection").GetSprite(GUIComponent.ComponentState.Hover);
screwSprites = GUI.Style.GetComponentStyle("ConnectionPanelScrew").Sprites[GUIComponent.ComponentState.None].Select(s => s.Sprite).ToList();
}
#endif
@@ -202,16 +202,17 @@ namespace Barotrauma.Items.Components
return -1;
}
public void TryAddLink(Wire wire)
public bool TryAddLink(Wire wire)
{
for (int i = 0; i < MaxLinked; i++)
{
if (wires[i] == null)
{
SetWire(i, wire);
return;
return true;
}
}
return false;
}
public void SetWire(int index, Wire wire)
@@ -64,7 +64,14 @@ namespace Barotrauma.Items.Components
partial void InitProjSpecific(XElement element);
private bool linksInitialized;
public override void OnMapLoaded()
{
if (linksInitialized) { return; }
InitializeLinks();
}
public void InitializeLinks()
{
foreach (Connection c in Connections)
{
@@ -90,6 +97,8 @@ namespace Barotrauma.Items.Components
}
}
}
linksInitialized = true;
}
public override void OnItemLoaded()
@@ -260,6 +269,7 @@ namespace Barotrauma.Items.Components
protected override void RemoveComponentSpecific()
{
base.RemoveComponentSpecific();
foreach (Wire wire in DisconnectedWires.ToList())
{
if (wire.OtherConnection(null) == null) //wire not connected to anything else
@@ -80,6 +80,8 @@ namespace Barotrauma.Items.Components
}
}
public override bool RecreateGUIOnResolutionChange => true;
private List<CustomInterfaceElement> customInterfaceElementList = new List<CustomInterfaceElement>();
public CustomInterface(Item item, XElement element)
@@ -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).", alwaysUseInstanceValues: true)]
[InGameEditable(FallBackTextTag = "connection.setcolor"), 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; }
@@ -45,7 +45,7 @@ namespace Barotrauma.Items.Components
fireInRange = IsFireInRange();
fireCheckTimer = FireCheckInterval;
}
item.SendSignal(0, fireInRange ? "1" : "0", "signal_out", null);
item.SendSignal(0, fireInRange ? Output : FalseOutput, "signal_out", null);
}
}
}
@@ -2,6 +2,7 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Xml.Linq;
@@ -205,11 +206,18 @@ namespace Barotrauma.Items.Components
Channel = newChannel;
}
break;
case "set_range":
if (float.TryParse(signal, NumberStyles.Float, CultureInfo.InvariantCulture, out float newRange))
{
Range = newRange;
}
break;
}
}
protected override void RemoveComponentSpecific()
{
base.RemoveComponentSpecific();
list.Remove(this);
}
}
@@ -59,6 +59,8 @@ namespace Barotrauma.Items.Components
private Vector2 sectionExtents;
private float currLength;
public bool Hidden;
private float removeNodeDelay;
@@ -287,7 +289,7 @@ namespace Barotrauma.Items.Components
Structure attachTarget = Structure.GetAttachTarget(item.WorldPosition);
canPlaceNode = attachTarget != null;
sub = sub ?? attachTarget?.Submarine;
sub ??= attachTarget?.Submarine;
Vector2 attachPos = GetAttachPosition(user);
newNodePos = sub == null ?
attachPos :
@@ -308,20 +310,25 @@ namespace Barotrauma.Items.Components
Vector2 prevNodePos = nodes[nodes.Count - 1];
if (sub != null) { prevNodePos += sub.HiddenSubPosition; }
float currLength = 0.0f;
currLength = 0.0f;
for (int i = 0; i < nodes.Count - 1; i++)
{
currLength += Vector2.Distance(nodes[i], nodes[i + 1]);
}
currLength += Vector2.Distance(nodes[nodes.Count - 1], newNodePos);
Vector2 itemPos = item.Position;
if (sub != null && user.Submarine == null) { prevNodePos += sub.Position; }
currLength += Vector2.Distance(prevNodePos, itemPos);
if (currLength > MaxLength)
{
Vector2 diff = nodes[nodes.Count - 1] - newNodePos;
Vector2 diff = prevNodePos - user.Position;
Vector2 pullBackDir = diff == Vector2.Zero ? Vector2.Zero : Vector2.Normalize(diff);
user.AnimController.Collider.ApplyForce(pullBackDir * user.Mass * 50.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
user.AnimController.UpdateUseItem(true, user.WorldPosition + pullBackDir * 200.0f);
Vector2 forceDir = pullBackDir;
if (!user.AnimController.InWater) { forceDir.Y = 0.0f; }
user.AnimController.Collider.ApplyForce(forceDir * user.Mass * 50.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity * 0.5f);
if (diff.LengthSquared() > 50.0f * 50.0f)
{
user.AnimController.UpdateUseItem(true, user.WorldPosition + pullBackDir * Math.Min(150.0f, diff.Length()));
}
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
{
@@ -564,7 +571,7 @@ namespace Barotrauma.Items.Components
int wireIndex = connections[i].FindWireIndex(item);
if (wireIndex == -1) { continue; }
#if SERVER
if (!connections[i].Item.Removed)
if (!connections[i].Item.Removed && (!connections[i].Item.Submarine?.Loading ?? true) && (!Level.Loaded?.Generating ?? true))
{
connections[i].Item.CreateServerEvent(connections[i].Item.GetComponent<ConnectionPanel>());
}
@@ -315,7 +315,8 @@ namespace Barotrauma.Items.Components
float springDamping = MathHelper.Lerp(SpringDampingLowSkill, SpringDampingHighSkill, degreeOfSuccess);
float rotationSpeed = MathHelper.Lerp(RotationSpeedLowSkill, RotationSpeedHighSkill, degreeOfSuccess);
if (user?.Info != null)
// Do not increase the weapons skill when operating a turret in an outpost level
if (user?.Info != null && (GameMain.GameSession?.Campaign == null || !Level.IsLoadedOutpost))
{
user.Info.IncreaseSkillLevel("weapons",
SkillSettings.Current.SkillIncreasePerSecondWhenOperatingTurret * deltaTime / Math.Max(user.GetSkillLevel("weapons"), 1.0f),
@@ -447,6 +448,11 @@ namespace Barotrauma.Items.Components
if (launchedProjectile != null || LaunchWithoutProjectile)
{
Launch(launchedProjectile?.Item, character);
if (item.AiTarget != null)
{
item.AiTarget.SoundRange = item.AiTarget.MaxSoundRange;
// Turrets also have a light component, which handles the sight range.
}
}
}
@@ -586,7 +592,7 @@ namespace Barotrauma.Items.Components
closestDist = maxDistance * maxDistance;
foreach (Submarine sub in Submarine.Loaded)
{
if (sub.Info.Type != SubmarineInfo.SubmarineType.Player) { continue; }
if (sub.Info.Type != SubmarineType.Player) { continue; }
float dist = Vector2.DistanceSquared(sub.WorldPosition, item.WorldPosition);
if (dist > closestDist) { continue; }
closestSub = sub;
@@ -224,7 +224,10 @@ namespace Barotrauma.Items.Components
if (variant == value) { return; }
#if SERVER
variant = value;
item.CreateServerEvent(this);
if (!item.Submarine?.Loading ?? true)
{
item.CreateServerEvent(this);
}
#elif CLIENT
Character character = picker;