Merge remote-tracking branch 'origin/master' into dev
# Conflicts: # Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs # Barotrauma/BarotraumaClient/ClientSource/Events/EventManager.cs # Barotrauma/BarotraumaClient/ClientSource/GUI/CrewManagement.cs # Barotrauma/BarotraumaClient/ClientSource/GUI/MedicalClinicUI.cs # Barotrauma/BarotraumaClient/ClientSource/GUI/Store.cs # Barotrauma/BarotraumaClient/ClientSource/GUI/SubmarineSelection.cs # Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/CampaignMode.cs # Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/MultiPlayerCampaign.cs # Barotrauma/BarotraumaClient/ClientSource/GameSession/RoundSummary.cs # Barotrauma/BarotraumaClient/ClientSource/Items/Components/Door.cs # Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Fabricator.cs # Barotrauma/BarotraumaClient/ClientSource/Items/Components/Projectile.cs # Barotrauma/BarotraumaClient/ClientSource/Map/Lights/ConvexHull.cs # Barotrauma/BarotraumaClient/ClientSource/Map/Lights/LightManager.cs # Barotrauma/BarotraumaClient/ClientSource/Map/Lights/LightSource.cs # Barotrauma/BarotraumaClient/ClientSource/Map/Structure.cs # Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs # Barotrauma/BarotraumaClient/ClientSource/Networking/Voip/VoipClient.cs # Barotrauma/BarotraumaClient/ClientSource/Networking/Voting.cs # Barotrauma/BarotraumaClient/ClientSource/Screens/ServerListScreen/ServerListScreen.cs # Barotrauma/BarotraumaClient/LinuxClient.csproj # Barotrauma/BarotraumaClient/MacClient.csproj # Barotrauma/BarotraumaClient/WindowsClient.csproj # Barotrauma/BarotraumaServer/LinuxServer.csproj # Barotrauma/BarotraumaServer/MacServer.csproj # Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs # Barotrauma/BarotraumaServer/ServerSource/GameSession/MedicalClinic.cs # Barotrauma/BarotraumaServer/ServerSource/Items/Inventory.cs # Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs # Barotrauma/BarotraumaServer/ServerSource/Utils/DoSProtection.cs # Barotrauma/BarotraumaServer/WindowsServer.csproj # Barotrauma/BarotraumaShared/SharedSource/Characters/AI/HumanAIController.cs # Barotrauma/BarotraumaShared/SharedSource/Characters/AI/IndoorsSteeringManager.cs # Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveContainItem.cs # Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveFindSafety.cs # Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveGetItem.cs # Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveGoTo.cs # Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveIdle.cs # Barotrauma/BarotraumaShared/SharedSource/Characters/AI/ShipCommand/ShipIssueWorkerOperateWeapons.cs # Barotrauma/BarotraumaShared/SharedSource/Characters/Attack.cs # Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs # Barotrauma/BarotraumaShared/SharedSource/Characters/Health/Afflictions/Affliction.cs # Barotrauma/BarotraumaShared/SharedSource/Characters/Health/Afflictions/AfflictionPrefab.cs # Barotrauma/BarotraumaShared/SharedSource/Characters/Health/CharacterHealth.cs # Barotrauma/BarotraumaShared/SharedSource/Characters/Params/CharacterParams.cs # Barotrauma/BarotraumaShared/SharedSource/ContentManagement/ContentPackageManager.cs # Barotrauma/BarotraumaShared/SharedSource/Enums.cs # Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/MissionAction.cs # Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/NPCChangeTeamAction.cs # Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/NPCFollowAction.cs # Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/NPCWaitAction.cs # Barotrauma/BarotraumaShared/SharedSource/Events/EventManager.cs # Barotrauma/BarotraumaShared/SharedSource/Events/Missions/Mission.cs # Barotrauma/BarotraumaShared/SharedSource/Events/Missions/PirateMission.cs # Barotrauma/BarotraumaShared/SharedSource/Events/Missions/SalvageMission.cs # Barotrauma/BarotraumaShared/SharedSource/GameSession/CargoManager.cs # Barotrauma/BarotraumaShared/SharedSource/GameSession/Data/Reputation.cs # Barotrauma/BarotraumaShared/SharedSource/GameSession/GameModes/CampaignMode.cs # Barotrauma/BarotraumaShared/SharedSource/GameSession/GameSession.cs # Barotrauma/BarotraumaShared/SharedSource/Items/Components/Door.cs # Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/LevelResource.cs # Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/RangedWeapon.cs # Barotrauma/BarotraumaShared/SharedSource/Items/Components/ItemContainer.cs # Barotrauma/BarotraumaShared/SharedSource/Items/Components/Projectile.cs # Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/LightComponent.cs # Barotrauma/BarotraumaShared/SharedSource/Items/Components/Turret.cs # Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs # Barotrauma/BarotraumaShared/SharedSource/Items/ItemPrefab.cs # Barotrauma/BarotraumaShared/SharedSource/Map/Gap.cs # Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Level.cs # Barotrauma/BarotraumaShared/SharedSource/Map/Map/Location.cs # Barotrauma/BarotraumaShared/SharedSource/Map/Map/Map.cs # Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostGenerator.cs # Barotrauma/BarotraumaShared/SharedSource/Map/Submarine.cs # Barotrauma/BarotraumaShared/SharedSource/Map/SubmarineBody.cs # Barotrauma/BarotraumaShared/SharedSource/Networking/ServerSettings.cs # Barotrauma/BarotraumaShared/SharedSource/Settings/GameSettings.cs # Barotrauma/BarotraumaShared/SharedSource/StatusEffects/PropertyConditional.cs # Barotrauma/BarotraumaShared/SharedSource/StatusEffects/StatusEffect.cs # Barotrauma/BarotraumaShared/changelog.txt
This commit is contained in:
@@ -1,13 +1,15 @@
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.IO;
|
||||
using Barotrauma.Networking;
|
||||
using FarseerPhysics;
|
||||
using FarseerPhysics.Dynamics;
|
||||
using FarseerPhysics.Dynamics.Joints;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Barotrauma.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
#if CLIENT
|
||||
using Barotrauma.Lights;
|
||||
#endif
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -243,10 +245,12 @@ namespace Barotrauma.Items.Components
|
||||
if (!target.item.Submarine.DockedTo.Contains(item.Submarine))
|
||||
{
|
||||
target.item.Submarine.ConnectedDockingPorts.Add(item.Submarine, target);
|
||||
target.item.Submarine.RefreshConnectedSubs();
|
||||
}
|
||||
if (!item.Submarine.DockedTo.Contains(target.item.Submarine))
|
||||
{
|
||||
item.Submarine.ConnectedDockingPorts.Add(target.item.Submarine, this);
|
||||
item.Submarine.RefreshConnectedSubs();
|
||||
}
|
||||
|
||||
DockingTarget = target;
|
||||
@@ -558,6 +562,7 @@ namespace Barotrauma.Items.Components
|
||||
var subs = new Submarine[] { item.Submarine, DockingTarget.item.Submarine };
|
||||
|
||||
bodies = new Body[4];
|
||||
RemoveConvexHulls();
|
||||
|
||||
if (DockingTarget.Door != null)
|
||||
{
|
||||
@@ -648,8 +653,10 @@ namespace Barotrauma.Items.Components
|
||||
hullRects[i].X -= expand;
|
||||
hullRects[i].Width += expand * 2;
|
||||
hullRects[i].Location -= MathUtils.ToPoint(subs[i].WorldPosition - subs[i].HiddenSubPosition);
|
||||
hulls[i] = new Hull(hullRects[i], subs[i]);
|
||||
hulls[i].RoomName = IsHorizontal ? "entityname.dockingport" : "entityname.dockinghatch";
|
||||
hulls[i] = new Hull(hullRects[i], subs[i])
|
||||
{
|
||||
RoomName = IsHorizontal ? "entityname.dockingport" : "entityname.dockinghatch"
|
||||
};
|
||||
hulls[i].AddToGrid(subs[i]);
|
||||
hulls[i].FreeID();
|
||||
|
||||
@@ -661,6 +668,15 @@ namespace Barotrauma.Items.Components
|
||||
BodyType.Static);
|
||||
}
|
||||
}
|
||||
#if CLIENT
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
convexHulls[i] =
|
||||
new ConvexHull(new Rectangle(
|
||||
new Point((int)item.Position.X, item.Rect.Y - item.Rect.Height * i),
|
||||
new Point((int)(DockingTarget.item.WorldPosition.X - item.WorldPosition.X), 0)), IsHorizontal, item);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (rightHullDiff <= 100 && hulls[0].Submarine != null)
|
||||
{
|
||||
@@ -764,15 +780,17 @@ namespace Barotrauma.Items.Components
|
||||
hullRects[1].Height += midHullDiff / 2 + 1;
|
||||
}
|
||||
|
||||
|
||||
int expand = 5;
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
hullRects[i].Y += expand;
|
||||
hullRects[i].Height += expand * 2;
|
||||
hullRects[i].Location -= MathUtils.ToPoint(subs[i].WorldPosition - subs[i].HiddenSubPosition);
|
||||
hulls[i] = new Hull(hullRects[i], subs[i]);
|
||||
hulls[i].RoomName = IsHorizontal ? "entityname.dockingport" : "entityname.dockinghatch";
|
||||
hulls[i] = new Hull(hullRects[i], subs[i])
|
||||
{
|
||||
RoomName = IsHorizontal ? "entityname.dockingport" : "entityname.dockinghatch",
|
||||
AvoidStaying = true
|
||||
};
|
||||
hulls[i].AddToGrid(subs[i]);
|
||||
hulls[i].FreeID();
|
||||
|
||||
@@ -784,6 +802,15 @@ namespace Barotrauma.Items.Components
|
||||
BodyType.Static);
|
||||
}
|
||||
}
|
||||
#if CLIENT
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
convexHulls[i] =
|
||||
new ConvexHull(new Rectangle(
|
||||
new Point(item.Rect.X + item.Rect.Width * i, (int)item.Position.Y),
|
||||
new Point(0, (int)(DockingTarget.item.WorldPosition.Y - item.WorldPosition.Y))), IsHorizontal, item);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (midHullDiff <= 100 && hulls[0].Submarine != null)
|
||||
{
|
||||
@@ -822,6 +849,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
partial void RemoveConvexHulls();
|
||||
|
||||
private void LinkHullsToGaps()
|
||||
{
|
||||
if (gap == null || hulls == null || hulls[0] == null || hulls[1] == null)
|
||||
@@ -916,7 +945,9 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
DockingTarget.item.Submarine.ConnectedDockingPorts.Remove(item.Submarine);
|
||||
DockingTarget.item.Submarine.RefreshConnectedSubs();
|
||||
item.Submarine.ConnectedDockingPorts.Remove(DockingTarget.item.Submarine);
|
||||
item.Submarine.RefreshConnectedSubs();
|
||||
|
||||
if (Door != null && DockingTarget.Door != null)
|
||||
{
|
||||
@@ -976,6 +1007,8 @@ namespace Barotrauma.Items.Components
|
||||
hulls[0]?.Remove(); hulls[0] = null;
|
||||
hulls[1]?.Remove(); hulls[1] = null;
|
||||
|
||||
RemoveConvexHulls();
|
||||
|
||||
if (gap != null)
|
||||
{
|
||||
gap.Remove();
|
||||
@@ -1091,6 +1124,7 @@ namespace Barotrauma.Items.Components
|
||||
hulls[0]?.Remove(); hulls[0] = null;
|
||||
hulls[1]?.Remove(); hulls[1] = null;
|
||||
gap?.Remove(); gap = null;
|
||||
RemoveConvexHulls();
|
||||
|
||||
overlaySprite?.Remove();
|
||||
overlaySprite = null;
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private bool isBroken;
|
||||
|
||||
public bool CanBeTraversed => (IsOpen || IsBroken) && !IsJammed && !IsStuck && !Impassable;
|
||||
public bool CanBeTraversed => !Impassable && (IsBroken || IsOpen);
|
||||
|
||||
public bool IsBroken
|
||||
{
|
||||
@@ -186,13 +186,19 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
get { return openState; }
|
||||
set
|
||||
{
|
||||
{
|
||||
openState = MathHelper.Clamp(value, 0.0f, 1.0f);
|
||||
#if CLIENT
|
||||
float size = IsHorizontal ? item.Rect.Width : item.Rect.Height;
|
||||
if (Math.Abs(lastConvexHullState - openState) * size < 5.0f) { return; }
|
||||
UpdateConvexHulls();
|
||||
lastConvexHullState = openState;
|
||||
//refresh convex hulls if the body of the door has moved by 5 pixels,
|
||||
//or if it becomes fully closed or fully open
|
||||
if (Math.Abs(lastConvexHullState - openState) * size > 5.0f ||
|
||||
(openState <= 0.0f && lastConvexHullState > 0.0f) ||
|
||||
(openState >= 1.0f && lastConvexHullState < 1.0f))
|
||||
{
|
||||
UpdateConvexHulls();
|
||||
lastConvexHullState = openState;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -383,6 +389,8 @@ namespace Barotrauma.Items.Components
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool isClosing = false;
|
||||
if ((!IsStuck && !IsJammed) || !isOpen)
|
||||
{
|
||||
@@ -521,8 +529,11 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
RefreshLinkedGap();
|
||||
#if CLIENT
|
||||
convexHull = new ConvexHull(Rectangle.Empty, !IsHorizontal, item);
|
||||
if (Window != Rectangle.Empty) { convexHull2 = new ConvexHull(Rectangle.Empty, !IsHorizontal, item); }
|
||||
convexHull = new ConvexHull(doorRect, IsHorizontal, item);
|
||||
if (Window != Rectangle.Empty)
|
||||
{
|
||||
convexHull2 = new ConvexHull(doorRect, IsHorizontal, item);
|
||||
}
|
||||
UpdateConvexHulls();
|
||||
#endif
|
||||
}
|
||||
@@ -556,6 +567,12 @@ namespace Barotrauma.Items.Components
|
||||
gap.ConnectedDoor = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (OutsideSubmarineFixture != null)
|
||||
{
|
||||
OutsideSubmarineFixture.Body.Remove(OutsideSubmarineFixture);
|
||||
OutsideSubmarineFixture = 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
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -96,11 +95,16 @@ namespace Barotrauma.Items.Components
|
||||
if (selectedEffect != null)
|
||||
{
|
||||
targetCharacter = character;
|
||||
ApplyStatusEffects(ActionType.OnWearing, 1.0f);
|
||||
ApplyStatusEffects(ActionType.OnWearing, 1.0f, targetCharacter);
|
||||
float selectedEffectStrength = GetCombinedEffectStrength();
|
||||
character.CharacterHealth.ApplyAffliction(null, selectedEffect.Instantiate(selectedEffectStrength));
|
||||
var affliction = character.CharacterHealth.GetAllAfflictions().FirstOrDefault(a => a.Prefab == selectedEffect);
|
||||
if (affliction != null) { affliction.Strength = selectedEffectStrength; }
|
||||
if (affliction != null)
|
||||
{
|
||||
affliction.Strength = selectedEffectStrength;
|
||||
//force strength to the correct value to bypass any clamping e.g. AfflictionHusk might be doing
|
||||
affliction.SetStrength(selectedEffectStrength);
|
||||
}
|
||||
#if SERVER
|
||||
item.CreateServerEvent(this);
|
||||
#endif
|
||||
@@ -110,7 +114,12 @@ namespace Barotrauma.Items.Components
|
||||
float selectedTaintedEffectStrength = GetCombinedTaintedEffectStrength();
|
||||
character.CharacterHealth.ApplyAffliction(null, selectedTaintedEffect.Instantiate(selectedTaintedEffectStrength));
|
||||
var affliction = character.CharacterHealth.GetAllAfflictions().FirstOrDefault(a => a.Prefab == selectedTaintedEffect);
|
||||
if (affliction != null) { affliction.Strength = selectedTaintedEffectStrength; }
|
||||
if (affliction != null)
|
||||
{
|
||||
affliction.Strength = selectedTaintedEffectStrength;
|
||||
//force strength to the correct value to bypass any clamping e.g. AfflictionHusk might be doing
|
||||
affliction.SetStrength(selectedTaintedEffectStrength);
|
||||
}
|
||||
targetCharacter = character;
|
||||
#if SERVER
|
||||
item.CreateServerEvent(this);
|
||||
@@ -127,7 +136,7 @@ namespace Barotrauma.Items.Components
|
||||
base.Update(deltaTime, cam);
|
||||
if (targetCharacter != null)
|
||||
{
|
||||
var rootContainer = item.GetRootContainer();
|
||||
var rootContainer = item.RootContainer;
|
||||
if (!targetCharacter.HasEquippedItem(item) &&
|
||||
(rootContainer == null || !targetCharacter.HasEquippedItem(rootContainer) || !targetCharacter.Inventory.IsInLimbSlot(rootContainer, InvSlotType.HealthInterface)))
|
||||
{
|
||||
@@ -220,7 +229,7 @@ namespace Barotrauma.Items.Components
|
||||
return MathHelper.Clamp(probability, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
private float GetTaintedProbabilityOnCombine(Character user)
|
||||
private static float GetTaintedProbabilityOnCombine(Character user)
|
||||
{
|
||||
if (user == null) { return 1.0f; }
|
||||
float probability = 1.0f - user.GetStatValue(StatTypes.GeneticMaterialTaintedProbabilityReductionOnCombine);
|
||||
|
||||
@@ -409,6 +409,7 @@ namespace Barotrauma.Items.Components
|
||||
private int leafVariants;
|
||||
private int[] flowerTiles;
|
||||
|
||||
[Serialize(100.0f, IsPropertySaveable.Yes)]
|
||||
public float Health
|
||||
{
|
||||
get => health;
|
||||
|
||||
@@ -321,12 +321,12 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override void Drop(Character dropper)
|
||||
public override void Drop(Character dropper, bool setTransform = true)
|
||||
{
|
||||
Drop(true, dropper);
|
||||
Drop(true, dropper, setTransform);
|
||||
}
|
||||
|
||||
private void Drop(bool dropConnectedWires, Character dropper)
|
||||
private void Drop(bool dropConnectedWires, Character dropper, bool setTransform = true)
|
||||
{
|
||||
GetRope()?.Snap();
|
||||
if (dropConnectedWires)
|
||||
@@ -343,8 +343,11 @@ namespace Barotrauma.Items.Components
|
||||
DeattachFromWall();
|
||||
}
|
||||
|
||||
if (Pusher != null) { Pusher.Enabled = false; }
|
||||
if (item.body != null) { item.body.Enabled = true; }
|
||||
if (setTransform)
|
||||
{
|
||||
if (Pusher != null) { Pusher.Enabled = false; }
|
||||
if (item.body != null) { item.body.Enabled = true; }
|
||||
}
|
||||
IsActive = false;
|
||||
attachTargetCell = null;
|
||||
|
||||
@@ -357,7 +360,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
item.Submarine = picker.Submarine;
|
||||
|
||||
if (item.body != null)
|
||||
if (item.body != null && setTransform)
|
||||
{
|
||||
if (item.body.Removed)
|
||||
{
|
||||
@@ -599,6 +602,10 @@ namespace Barotrauma.Items.Components
|
||||
throw new InvalidOperationException($"Tried to attach an item with no physics body to a wall ({item.Prefab.Identifier}).");
|
||||
}
|
||||
|
||||
body.Enabled = false;
|
||||
body.SetTransformIgnoreContacts(body.SimPosition, rotation: 0.0f);
|
||||
item.body = null;
|
||||
|
||||
//outside hulls/subs -> we need to check if the item is being attached on a structure outside the sub
|
||||
if (item.CurrentHull == null && item.Submarine == null)
|
||||
{
|
||||
@@ -638,9 +645,6 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
body.Enabled = false;
|
||||
item.body = null;
|
||||
|
||||
DisplayMsg = prevMsg;
|
||||
PickKey = prevPickKey;
|
||||
requiredItems = new Dictionary<RelatedItem.RelationType, List<RelatedItem>>(prevRequiredItems);
|
||||
@@ -700,7 +704,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
Vector2 attachPos = GetAttachPosition(character, useWorldCoordinates: true);
|
||||
Submarine attachSubmarine = Structure.GetAttachTarget(attachPos)?.Submarine ?? item.Submarine;
|
||||
int maxAttachableCount = (int)character.Info.GetSavedStatValue(StatTypes.MaxAttachableCount, item.Prefab.Identifier);
|
||||
int maxAttachableCount = (int)character.Info.GetSavedStatValueWithBotsInMp(StatTypes.MaxAttachableCount, item.Prefab.Identifier);
|
||||
|
||||
int currentlyAttachedCount = Item.ItemList.Count(
|
||||
i => i.Submarine == attachSubmarine && i.GetComponent<Holdable>() is Holdable holdable && holdable.Attached && i.Prefab.Identifier == item.Prefab.Identifier);
|
||||
if (maxAttachableCount == 0)
|
||||
@@ -811,7 +816,7 @@ namespace Barotrauma.Items.Components
|
||||
foreach (var edge in cell.Edges)
|
||||
{
|
||||
if (!edge.IsSolid) { continue; }
|
||||
if (MathUtils.GetLineIntersection(edge.Point1, edge.Point2, user.WorldPosition, attachPos, out Vector2 intersection))
|
||||
if (MathUtils.GetLineSegmentIntersection(edge.Point1, edge.Point2, user.WorldPosition, attachPos, out Vector2 intersection))
|
||||
{
|
||||
attachPos = intersection;
|
||||
edgeFound = true;
|
||||
|
||||
+14
-5
@@ -97,11 +97,18 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (holdable != null && !holdable.Attached)
|
||||
{
|
||||
trigger.Enabled = false;
|
||||
if (trigger != null)
|
||||
{
|
||||
trigger.Enabled = false;
|
||||
}
|
||||
IsActive = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (trigger == null)
|
||||
{
|
||||
CreateTriggerBody();
|
||||
}
|
||||
if (trigger != null && Vector2.DistanceSquared(item.SimPosition, trigger.SimPosition) > 0.01f)
|
||||
{
|
||||
trigger.SetTransform(item.SimPosition, 0.0f);
|
||||
@@ -123,12 +130,15 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
holdable.PickingTime = float.MaxValue;
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateTriggerBody()
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(trigger == null, "LevelResource trigger already created!");
|
||||
var body = item.body ?? holdable.Body;
|
||||
|
||||
if (body != null)
|
||||
if (body != null && Attached)
|
||||
{
|
||||
trigger = new PhysicsBody(body.Width, body.Height, body.Radius,
|
||||
trigger = new PhysicsBody(body.Width, body.Height, body.Radius,
|
||||
body.Density,
|
||||
BodyType.Static,
|
||||
Physics.CollisionWall,
|
||||
@@ -143,7 +153,6 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
protected override void RemoveComponentSpecific()
|
||||
{
|
||||
base.RemoveComponentSpecific();
|
||||
if (trigger != null)
|
||||
{
|
||||
trigger.Remove();
|
||||
|
||||
@@ -170,9 +170,9 @@ namespace Barotrauma.Items.Components
|
||||
return characterUsable || character == null;
|
||||
}
|
||||
|
||||
public override void Drop(Character dropper)
|
||||
public override void Drop(Character dropper, bool setTransform = true)
|
||||
{
|
||||
base.Drop(dropper);
|
||||
base.Drop(dropper, setTransform);
|
||||
hitting = false;
|
||||
hitPos = 0.0f;
|
||||
}
|
||||
|
||||
@@ -241,12 +241,9 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override void Drop(Character dropper)
|
||||
public override void Drop(Character dropper, bool setTransform = true)
|
||||
{
|
||||
if (picker == null)
|
||||
{
|
||||
picker = dropper;
|
||||
}
|
||||
picker ??= dropper;
|
||||
|
||||
Vector2 bodyDropPos = Vector2.Zero;
|
||||
|
||||
@@ -255,8 +252,7 @@ namespace Barotrauma.Items.Components
|
||||
if (item.ParentInventory != null && item.ParentInventory.Owner != null && !item.ParentInventory.Owner.Removed)
|
||||
{
|
||||
bodyDropPos = item.ParentInventory.Owner.SimPosition;
|
||||
|
||||
if (item.body != null) item.body.ResetDynamics();
|
||||
item.body?.ResetDynamics();
|
||||
}
|
||||
}
|
||||
else if (!picker.Removed)
|
||||
@@ -270,7 +266,7 @@ namespace Barotrauma.Items.Components
|
||||
picker = null;
|
||||
}
|
||||
|
||||
if (item.body != null && !item.body.Enabled)
|
||||
if (item.body != null && !item.body.Enabled && setTransform)
|
||||
{
|
||||
if (item.body.Removed)
|
||||
{
|
||||
|
||||
@@ -260,7 +260,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
Vector2 barrelPos = TransformedBarrelPos + item.body.SimPosition;
|
||||
float rotation = (Item.body.Dir == 1.0f) ? Item.body.Rotation : Item.body.Rotation - MathHelper.Pi;
|
||||
float spread = GetSpread(character) * Projectile.GetSpreadFromPool();
|
||||
float spread = GetSpread(character) * projectile.GetSpreadFromPool();
|
||||
|
||||
var lastProjectile = LastProjectile;
|
||||
if (lastProjectile != projectile)
|
||||
@@ -277,7 +277,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
Item.body.ApplyLinearImpulse(new Vector2((float)Math.Cos(projectile.Item.body.Rotation), (float)Math.Sin(projectile.Item.body.Rotation)) * Item.body.Mass * -50.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
|
||||
}
|
||||
projectile.Item.body.ApplyTorque(projectile.Item.body.Mass * degreeOfFailure * 20.0f * Projectile.GetSpreadFromPool());
|
||||
projectile.Item.body.ApplyTorque(projectile.Item.body.Mass * degreeOfFailure * 20.0f * projectile.GetSpreadFromPool());
|
||||
}
|
||||
Item.RemoveContained(projectile.Item);
|
||||
}
|
||||
|
||||
@@ -84,6 +84,9 @@ namespace Barotrauma.Items.Components
|
||||
[Serialize(false, IsPropertySaveable.No, description: "Can the item repair multiple things at once, or will it only affect the first thing the ray from the barrel hits.")]
|
||||
public bool RepairMultiple { get; set; }
|
||||
|
||||
[Serialize(true, IsPropertySaveable.No, description: "Can the item repair multiple walls at once? Only relevant if RepairMultiple is true.")]
|
||||
public bool RepairMultipleWalls { get; set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No, description: "Can the item repair things through holes in walls.")]
|
||||
public bool RepairThroughHoles { get; set; }
|
||||
|
||||
@@ -383,6 +386,7 @@ namespace Barotrauma.Items.Components
|
||||
//stop the ray if it already hit a door/wall and is now about to hit some other type of entity
|
||||
if (lastHitType == typeof(Item) || lastHitType == typeof(Structure)) { break; }
|
||||
}
|
||||
if (!RepairMultipleWalls && (bodyType == typeof(Structure) || (body.UserData as Item)?.GetComponent<Door>() != null)) { break; }
|
||||
|
||||
Character hitCharacter = null;
|
||||
if (body.UserData is Limb limb)
|
||||
|
||||
@@ -56,9 +56,9 @@ namespace Barotrauma.Items.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void Drop(Character dropper)
|
||||
public override void Drop(Character dropper, bool setTransform = true)
|
||||
{
|
||||
base.Drop(dropper);
|
||||
base.Drop(dropper, setTransform);
|
||||
throwState = ThrowState.None;
|
||||
throwAngle = ThrowAngleStart;
|
||||
Item.ResetWaterDragCoefficient();
|
||||
|
||||
@@ -237,7 +237,7 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(0f, IsPropertySaveable.No, description: "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). Note that there's also a generic BotPriority for all item prefabs.")]
|
||||
[Serialize(0f, IsPropertySaveable.No, description: "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 forced). Note that there's also a generic BotPriority for all item prefabs.")]
|
||||
public float CombatPriority { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -435,7 +435,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
/// <summary>a Character has dropped the item</summary>
|
||||
public virtual void Drop(Character dropper) { }
|
||||
public virtual void Drop(Character dropper, bool setTransform = true) { }
|
||||
|
||||
/// <returns>true if the operation was completed</returns>
|
||||
public virtual bool CrewAIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
|
||||
@@ -682,9 +682,10 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public virtual void FlipY(bool relativeToSub) { }
|
||||
|
||||
public bool IsNotEmpty(Character user, bool checkContainedItems = true) =>
|
||||
HasRequiredContainedItems(user, addMessage: false) &&
|
||||
(!checkContainedItems || Item.OwnInventory == null || Item.OwnInventory.AllItems.Any(i => i.Condition > 0));
|
||||
/// <summary>
|
||||
/// Shorthand for !HasRequiredContainedItems()
|
||||
/// </summary>
|
||||
public bool IsEmpty(Character user) => !HasRequiredContainedItems(user, addMessage: false);
|
||||
|
||||
public bool HasRequiredContainedItems(Character user, bool addMessage, LocalizedString msg = null)
|
||||
{
|
||||
@@ -716,20 +717,50 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (character.IsBot && item.IgnoreByAI(character)) { return false; }
|
||||
if (!item.IsInteractable(character)) { return false; }
|
||||
if (requiredItems.None()) { return true; }
|
||||
if (character.Inventory != null)
|
||||
if (requiredItems.Count == 0) { return true; }
|
||||
if (character.Inventory != null && requiredItems.TryGetValue(RelatedItem.RelationType.Picked, out List<RelatedItem> relatedItems))
|
||||
{
|
||||
foreach (Item item in character.Inventory.AllItems)
|
||||
foreach (RelatedItem relatedItem in relatedItems)
|
||||
{
|
||||
if (requiredItems.Any(ri => ri.Value.Any(r => r.Type == RelatedItem.RelationType.Picked && r.MatchesItem(item))))
|
||||
foreach (Item otherItem in character.Inventory.AllItems)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (relatedItem.MatchesItem(otherItem))
|
||||
{
|
||||
if (otherItem.GetComponent<IdCard>() is IdCard idCard)
|
||||
{
|
||||
if (!CheckIdCardAccess(relatedItem, idCard))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Presumes that matching is already checked.
|
||||
/// </summary>
|
||||
private bool CheckIdCardAccess(RelatedItem relatedItem, IdCard idCard)
|
||||
{
|
||||
if (item.Submarine != null)
|
||||
{
|
||||
//id cards don't work in enemy subs (except on items that only require the default "idcard" tag)
|
||||
if (idCard.TeamID != CharacterTeamType.None && idCard.TeamID != item.Submarine.TeamID && relatedItem.Identifiers.Any(id => id != "idcard"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (idCard.SubmarineSpecificID != 0 && item.Submarine.SubmarineSpecificIDTag != idCard.SubmarineSpecificID)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual bool HasRequiredItems(Character character, bool addMessage, LocalizedString msg = null)
|
||||
{
|
||||
if (requiredItems.None()) { return true; }
|
||||
@@ -765,23 +796,14 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
bool CheckItems(RelatedItem relatedItem, IEnumerable<Item> itemList)
|
||||
{
|
||||
bool Predicate(Item it)
|
||||
bool Predicate(Item it)
|
||||
{
|
||||
if (it == null || it.Condition <= 0.0f || !relatedItem.MatchesItem(it)) { return false; }
|
||||
if (item.Submarine != null)
|
||||
if (it.GetComponent<IdCard>() is IdCard idCard)
|
||||
{
|
||||
var idCard = it.GetComponent<IdCard>();
|
||||
if (idCard != null)
|
||||
if (!CheckIdCardAccess(relatedItem, idCard))
|
||||
{
|
||||
//id cards don't work in enemy subs (except on items that only require the default "idcard" tag)
|
||||
if (idCard.TeamID != CharacterTeamType.None && idCard.TeamID != item.Submarine.TeamID && relatedItem.Identifiers.Any(id => id != "idcard"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (idCard.SubmarineSpecificID != 0 && item.Submarine.SubmarineSpecificIDTag != idCard.SubmarineSpecificID)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -20,11 +20,13 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
public readonly int MaxStackSize;
|
||||
public List<RelatedItem> ContainableItems;
|
||||
public readonly bool AutoInject;
|
||||
|
||||
public SlotRestrictions(int maxStackSize, List<RelatedItem> containableItems)
|
||||
public SlotRestrictions(int maxStackSize, List<RelatedItem> containableItems, bool autoInject)
|
||||
{
|
||||
MaxStackSize = maxStackSize;
|
||||
ContainableItems = containableItems;
|
||||
AutoInject = autoInject;
|
||||
}
|
||||
|
||||
public bool MatchesItem(Item item)
|
||||
@@ -269,7 +271,7 @@ namespace Barotrauma.Items.Components
|
||||
List<SlotRestrictions> newSlotRestrictions = new List<SlotRestrictions>(totalCapacity);
|
||||
for (int i = 0; i < capacity; i++)
|
||||
{
|
||||
newSlotRestrictions.Add(new SlotRestrictions(maxStackSize, ContainableItems));
|
||||
newSlotRestrictions.Add(new SlotRestrictions(maxStackSize, ContainableItems, autoInject: false));
|
||||
}
|
||||
|
||||
int subContainerIndex = capacity;
|
||||
@@ -279,6 +281,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
int subCapacity = subElement.GetAttributeInt("capacity", 1);
|
||||
int subMaxStackSize = subElement.GetAttributeInt("maxstacksize", maxStackSize);
|
||||
bool autoInject = subElement.GetAttributeBool("autoinject", false);
|
||||
|
||||
var subContainableItems = new List<RelatedItem>();
|
||||
foreach (var subSubElement in subElement.Elements())
|
||||
@@ -298,7 +301,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
for (int i = subContainerIndex; i < subContainerIndex + subCapacity; i++)
|
||||
{
|
||||
newSlotRestrictions.Add(new SlotRestrictions(subMaxStackSize, subContainableItems));
|
||||
newSlotRestrictions.Add(new SlotRestrictions(subMaxStackSize, subContainableItems, autoInject));
|
||||
}
|
||||
subContainerIndex += subCapacity;
|
||||
}
|
||||
@@ -466,7 +469,7 @@ namespace Barotrauma.Items.Components
|
||||
prevContainedItemPositions = item.Position;
|
||||
}
|
||||
|
||||
if (AutoInject)
|
||||
if (AutoInject || slotRestrictions.Any(s => s.AutoInject))
|
||||
{
|
||||
//normally autoinjection should delete the (medical) item, so it only gets applied once
|
||||
//but in multiplayer clients aren't allowed to remove items themselves, so they may be able to trigger this dozens of times
|
||||
@@ -480,7 +483,21 @@ namespace Barotrauma.Items.Components
|
||||
ownerCharacter.HealthPercentage / 100f <= AutoInjectThreshold &&
|
||||
ownerCharacter.HasEquippedItem(item))
|
||||
{
|
||||
foreach (Item item in Inventory.AllItemsMod)
|
||||
if (AutoInject)
|
||||
{
|
||||
Inventory.AllItemsMod.ForEach(i => Inject(i));
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < slotRestrictions.Length; i++)
|
||||
{
|
||||
if (slotRestrictions[i].AutoInject)
|
||||
{
|
||||
Inventory.GetItemsAt(i).ForEachMod(i => Inject(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
void Inject(Item item)
|
||||
{
|
||||
item.ApplyStatusEffects(ActionType.OnSuccess, 1.0f, ownerCharacter, useTarget: ownerCharacter);
|
||||
item.ApplyStatusEffects(ActionType.OnUse, 1.0f, ownerCharacter, useTarget: ownerCharacter);
|
||||
@@ -511,25 +528,28 @@ namespace Barotrauma.Items.Components
|
||||
if (activeContainedItem.ExcludeFullCondition && contained.IsFullCondition) { continue; }
|
||||
StatusEffect effect = activeContainedItem.StatusEffect;
|
||||
|
||||
targets.Clear();
|
||||
bool wearing = item.GetComponent<Wearable>() is Wearable { IsActive: true };
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.This))
|
||||
{
|
||||
effect.Apply(ActionType.OnContaining, deltaTime, item, item.AllPropertyObjects);
|
||||
targets.AddRange(item.AllPropertyObjects);
|
||||
}
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.Contained))
|
||||
{
|
||||
effect.Apply(ActionType.OnContaining, deltaTime, item, contained.AllPropertyObjects);
|
||||
targets.AddRange(contained.AllPropertyObjects);
|
||||
}
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.Character) && item.ParentInventory?.Owner is Character character)
|
||||
{
|
||||
effect.Apply(ActionType.OnContaining, deltaTime, item, character);
|
||||
targets.Add(character);
|
||||
}
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.NearbyItems) ||
|
||||
effect.HasTargetType(StatusEffect.TargetType.NearbyCharacters))
|
||||
{
|
||||
targets.Clear();
|
||||
effect.AddNearbyTargets(item.WorldPosition, targets);
|
||||
effect.Apply(ActionType.OnActive, deltaTime, item, targets);
|
||||
}
|
||||
effect.Apply(ActionType.OnActive, deltaTime, item, targets);
|
||||
effect.Apply(ActionType.OnContaining, deltaTime, item, targets);
|
||||
if (wearing) { effect.Apply(ActionType.OnWearing, deltaTime, item, targets); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -629,7 +649,7 @@ namespace Barotrauma.Items.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void Drop(Character dropper)
|
||||
public override void Drop(Character dropper, bool setTransform = true)
|
||||
{
|
||||
IsActive = true;
|
||||
SetContainedActive(false);
|
||||
|
||||
@@ -98,6 +98,20 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(true, IsPropertySaveable.No, description: "Can another character select this controller when another character has already selected it?")]
|
||||
public bool AllowSelectingWhenSelectedByOther
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(true, IsPropertySaveable.No, description: "Can another character select this controller when a bot has already selected it?")]
|
||||
public bool AllowSelectingWhenSelectedByBot
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public bool ControlCharacterPose
|
||||
{
|
||||
get { return limbPositions.Count > 0; }
|
||||
@@ -466,8 +480,18 @@ namespace Barotrauma.Items.Components
|
||||
IsActive = false;
|
||||
CancelUsing(user);
|
||||
user = null;
|
||||
return false;
|
||||
}
|
||||
else if (user.IsBot && !activator.IsBot)
|
||||
{
|
||||
if (AllowSelectingWhenSelectedByBot)
|
||||
{
|
||||
CancelUsing(user);
|
||||
user = activator;
|
||||
IsActive = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return AllowSelectingWhenSelectedByOther;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -10,6 +10,18 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
private float force;
|
||||
|
||||
/// <summary>
|
||||
/// Latest signal the set_force connection received, used to set <see cref="targetForce"/> in the Update method.
|
||||
/// We use a separate variable, because otherwise specific item update orders and sending multiple signals to set_force would lead to bugs:
|
||||
/// targetForce could be set to 0, then a power grid might update as if the engine was off and mark the voltage of the grid as 1,
|
||||
/// then another item could set the targetForce to 100 and make it run without power.
|
||||
/// </summary>
|
||||
private float? lastReceivedTargetForce;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of force the engine is aiming for (the actual force may be less than this,
|
||||
/// depending on the amount of power, the condition of the engine or boosts from talents)
|
||||
/// </summary>
|
||||
private float targetForce;
|
||||
|
||||
private float maxForce;
|
||||
@@ -58,7 +70,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public float CurrentVolume
|
||||
{
|
||||
get { return Math.Abs((force / 100.0f) * (MinVoltage <= 0.0f ? 1.0f : Math.Min(prevVoltage / MinVoltage, 1.0f))); }
|
||||
get { return Math.Abs((force / 100.0f) * (MinVoltage <= 0.0f ? 1.0f : Math.Min(prevVoltage, 1.0f))); }
|
||||
}
|
||||
|
||||
public float CurrentBrokenVolume
|
||||
@@ -110,7 +122,10 @@ namespace Barotrauma.Items.Components
|
||||
hasPower = Voltage > MinVoltage;
|
||||
}
|
||||
|
||||
|
||||
if (lastReceivedTargetForce.HasValue)
|
||||
{
|
||||
targetForce = lastReceivedTargetForce.Value;
|
||||
}
|
||||
Force = MathHelper.Lerp(force, (Voltage < MinVoltage) ? 0.0f : targetForce, deltaTime * 10.0f);
|
||||
if (Math.Abs(Force) > 1.0f)
|
||||
{
|
||||
@@ -254,7 +269,7 @@ namespace Barotrauma.Items.Components
|
||||
if (float.TryParse(signal.value, NumberStyles.Float, CultureInfo.InvariantCulture, out float tempForce))
|
||||
{
|
||||
controlLockTimer = 0.1f;
|
||||
targetForce = MathHelper.Clamp(tempForce, -100.0f, 100.0f);
|
||||
lastReceivedTargetForce = MathHelper.Clamp(tempForce, -100.0f, 100.0f);
|
||||
User = signal.sender;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -784,8 +784,10 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
float condition1 = MathUtils.IsValid(item1.Condition) ? item1.Condition : 0;
|
||||
float condition2 = MathUtils.IsValid(item2.Condition) ? item2.Condition : 0;
|
||||
//prefer items in worse condition
|
||||
return Math.Sign(item2.Condition - item1.Condition);
|
||||
return Math.Sign(condition2 - condition1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -775,7 +775,6 @@ namespace Barotrauma.Items.Components
|
||||
if (shutDown)
|
||||
{
|
||||
PowerOn = false;
|
||||
AutoTemp = false;
|
||||
TargetFissionRate = 0.0f;
|
||||
TargetTurbineOutput = 0.0f;
|
||||
unsentChanges = true;
|
||||
|
||||
@@ -68,7 +68,6 @@ namespace Barotrauma.Items.Components
|
||||
public bool UseDirectionalPing => useDirectionalPing;
|
||||
private bool useDirectionalPing = false;
|
||||
private Vector2 pingDirection = new Vector2(1.0f, 0.0f);
|
||||
private bool useMineralScanner;
|
||||
|
||||
private bool aiPingCheckPending;
|
||||
|
||||
@@ -133,6 +132,9 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(true, IsPropertySaveable.Yes, alwaysUseInstanceValues: true)]
|
||||
public bool UseMineralScanner { get; set; }
|
||||
|
||||
public float Zoom
|
||||
{
|
||||
get { return zoom; }
|
||||
@@ -366,7 +368,7 @@ namespace Barotrauma.Items.Components
|
||||
bool isActive = msg.ReadBoolean();
|
||||
bool directionalPing = useDirectionalPing;
|
||||
float zoomT = zoom, pingDirectionT = 0.0f;
|
||||
bool mineralScanner = useMineralScanner;
|
||||
bool mineralScanner = UseMineralScanner;
|
||||
if (isActive)
|
||||
{
|
||||
zoomT = msg.ReadRangedSingle(0.0f, 1.0f, 8);
|
||||
@@ -391,13 +393,13 @@ namespace Barotrauma.Items.Components
|
||||
float pingAngle = MathHelper.Lerp(0.0f, MathHelper.TwoPi, pingDirectionT);
|
||||
pingDirection = new Vector2((float)Math.Cos(pingAngle), (float)Math.Sin(pingAngle));
|
||||
}
|
||||
useMineralScanner = mineralScanner;
|
||||
UseMineralScanner = mineralScanner;
|
||||
#if CLIENT
|
||||
zoomSlider.BarScroll = zoomT;
|
||||
directionalModeSwitch.Selected = useDirectionalPing;
|
||||
if (mineralScannerSwitch != null)
|
||||
{
|
||||
mineralScannerSwitch.Selected = useMineralScanner;
|
||||
mineralScannerSwitch.Selected = UseMineralScanner;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -418,7 +420,7 @@ namespace Barotrauma.Items.Components
|
||||
float pingAngle = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(pingDirection));
|
||||
msg.WriteRangedSingle(MathUtils.InverseLerp(0.0f, MathHelper.TwoPi, pingAngle), 0.0f, 1.0f, 8);
|
||||
}
|
||||
msg.WriteBoolean(useMineralScanner);
|
||||
msg.WriteBoolean(UseMineralScanner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -444,7 +444,7 @@ namespace Barotrauma.Items.Components
|
||||
if (connectedSubUpdateTimer <= 0.0f)
|
||||
{
|
||||
connectedSubs.Clear();
|
||||
connectedSubs = controlledSub?.GetConnectedSubs();
|
||||
connectedSubs.AddRange(controlledSub.GetConnectedSubs());
|
||||
connectedSubUpdateTimer = ConnectedSubUpdateInterval;
|
||||
}
|
||||
|
||||
@@ -535,7 +535,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
foreach (GraphEdge edge in cell.Edges)
|
||||
{
|
||||
if (MathUtils.GetLineIntersection(edge.Point1 + cell.Translation, edge.Point2 + cell.Translation, controlledSub.WorldPosition, cell.Center, out Vector2 intersection))
|
||||
if (MathUtils.GetLineSegmentIntersection(edge.Point1 + cell.Translation, edge.Point2 + cell.Translation, controlledSub.WorldPosition, cell.Center, out Vector2 intersection))
|
||||
{
|
||||
Vector2 diff = controlledSub.WorldPosition - intersection;
|
||||
//far enough -> ignore
|
||||
|
||||
@@ -435,7 +435,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
//other junction boxes don't need to receive the signal in the pass-through signal connections
|
||||
//because we relay it straight to the connected items without going through the whole chain of junction boxes
|
||||
if (ic is PowerTransfer && !(ic is RelayComponent)) { continue; }
|
||||
if (ic is PowerTransfer && ic is not RelayComponent) { continue; }
|
||||
ic.ReceiveSignal(signal, recipient);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,18 +14,18 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class Projectile : ItemComponent, IServerSerializable
|
||||
{
|
||||
const int SpreadCounterWrapAround = 256;
|
||||
|
||||
private static readonly ImmutableArray<float> spreadPool;
|
||||
static Projectile()
|
||||
{
|
||||
MTRandom random = new MTRandom(0);
|
||||
spreadPool = Enumerable.Range(0, SpreadCounterWrapAround).Select(f => (float)random.NextDouble() - 0.5f).ToImmutableArray();
|
||||
spreadPool = Enumerable.Range(0, byte.MaxValue + 1).Select(f => (float)random.NextDouble() - 0.5f).ToImmutableArray();
|
||||
}
|
||||
|
||||
public static float GetSpreadFromPool()
|
||||
public static byte SpreadCounter { get; private set; }
|
||||
|
||||
public static void ResetSpreadCounter()
|
||||
{
|
||||
return spreadPool[SpreadCounter];
|
||||
SpreadCounter = 0;
|
||||
}
|
||||
|
||||
struct HitscanResult
|
||||
@@ -62,7 +62,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private bool removePending;
|
||||
|
||||
public static byte SpreadCounter { get; private set; }
|
||||
private byte spreadIndex;
|
||||
|
||||
//continuous collision detection is used while the projectile is moving faster than this
|
||||
const float ContinuousCollisionThreshold = 5.0f;
|
||||
@@ -299,7 +299,8 @@ namespace Barotrauma.Items.Components
|
||||
return;
|
||||
}
|
||||
|
||||
SpreadCounter = (byte)(item.ID % SpreadCounterWrapAround);
|
||||
spreadIndex = SpreadCounter;
|
||||
SpreadCounter++;
|
||||
|
||||
InitProjSpecific(element);
|
||||
}
|
||||
@@ -328,6 +329,12 @@ namespace Barotrauma.Items.Components
|
||||
originalCollisionTargets = item.body.CollidesWith;
|
||||
}
|
||||
|
||||
public float GetSpreadFromPool()
|
||||
{
|
||||
spreadIndex = (byte)MathUtils.PositiveModulo(spreadIndex, spreadPool.Length);
|
||||
return spreadPool[spreadIndex];
|
||||
}
|
||||
|
||||
private void Launch(Character user, Vector2 simPosition, float rotation, float damageMultiplier = 1f, float launchImpulseModifier = 0f)
|
||||
{
|
||||
if (Item.body == null) { return; }
|
||||
@@ -380,7 +387,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
#if SERVER
|
||||
launchRot = rotation;
|
||||
Item.CreateServerEvent(this, new EventData(launch: true, spreadCounter: (byte)(SpreadCounter - 1)));
|
||||
Item.CreateServerEvent(this, new EventData(launch: true, spreadCounter: (byte)(spreadIndex - 1)));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -396,7 +403,6 @@ namespace Barotrauma.Items.Components
|
||||
for (int i = 0; i < HitScanCount; i++)
|
||||
{
|
||||
float launchAngle;
|
||||
|
||||
if (StaticSpread)
|
||||
{
|
||||
launchAngle = initialRotation + MathHelper.ToRadians(i - ((float)(HitScanCount - 1) / 2)) * Spread;
|
||||
@@ -405,7 +411,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
launchAngle = initialRotation + MathHelper.ToRadians(Spread * GetSpreadFromPool());
|
||||
}
|
||||
SpreadCounter++;
|
||||
spreadIndex++;
|
||||
|
||||
Vector2 launchDir = new Vector2((float)Math.Cos(launchAngle), (float)Math.Sin(launchAngle));
|
||||
if (Hitscan)
|
||||
@@ -703,7 +709,7 @@ namespace Barotrauma.Items.Components
|
||||
return hits;
|
||||
}
|
||||
|
||||
public override void Drop(Character dropper)
|
||||
public override void Drop(Character dropper, bool setTransform = true)
|
||||
{
|
||||
Item.ResetWaterDragCoefficient();
|
||||
if (dropper != null)
|
||||
@@ -711,7 +717,7 @@ namespace Barotrauma.Items.Components
|
||||
DisableProjectileCollisions();
|
||||
Unstick();
|
||||
}
|
||||
base.Drop(dropper);
|
||||
base.Drop(dropper, setTransform);
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
@@ -885,17 +891,8 @@ namespace Barotrauma.Items.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector2 normalizedVel;
|
||||
Vector2 dir;
|
||||
if (item.body.LinearVelocity.LengthSquared() < 0.001f)
|
||||
{
|
||||
normalizedVel = Vector2.Zero;
|
||||
dir = contact.Manifold.LocalNormal;
|
||||
}
|
||||
else
|
||||
{
|
||||
normalizedVel = dir = Vector2.Normalize(item.body.LinearVelocity);
|
||||
}
|
||||
Vector2 dir = item.body.LinearVelocity.LengthSquared() < 0.001f ?
|
||||
contact.Manifold.LocalNormal : Vector2.Normalize(item.body.LinearVelocity);
|
||||
|
||||
//do a raycast in the sub's coordinate space to see if it hit a structure
|
||||
var wallBody = Submarine.PickBody(
|
||||
@@ -904,7 +901,7 @@ namespace Barotrauma.Items.Components
|
||||
collisionCategory: Physics.CollisionWall);
|
||||
if (wallBody?.FixtureList?.First() != null && (wallBody.UserData is Structure || wallBody.UserData is Item) &&
|
||||
//ignore the hit if it's behind the position the item was launched from, and the projectile is travelling in the opposite direction
|
||||
Vector2.Dot((item.body.SimPosition + normalizedVel) - launchPos, dir) > 0)
|
||||
Vector2.Dot(item.body.SimPosition - launchPos, dir) > 0)
|
||||
{
|
||||
target = wallBody.FixtureList.First();
|
||||
if (hits.Contains(target.Body))
|
||||
@@ -942,7 +939,7 @@ namespace Barotrauma.Items.Components
|
||||
Character character = null;
|
||||
if (target.Body.UserData is Submarine submarine && target.UserData is not Barotrauma.Item)
|
||||
{
|
||||
item.Move(-submarine.Position);
|
||||
item.Move(-submarine.Position, ignoreContacts: false);
|
||||
item.Submarine = submarine;
|
||||
item.body.Submarine = submarine;
|
||||
return !Hitscan;
|
||||
|
||||
@@ -339,18 +339,19 @@ namespace Barotrauma.Items.Components
|
||||
if (Math.Abs(TargetPullForce) > 0.001f)
|
||||
{
|
||||
var targetBody = GetBodyToPull(target);
|
||||
if (user != null && targetCharacter != null && !user.AnimController.InWater)
|
||||
bool lerpForces = LerpForces;
|
||||
if (!lerpForces && user != null && targetCharacter != null && !user.AnimController.InWater)
|
||||
{
|
||||
// Prevents rubberbanding horizontally when dragging a corpse.
|
||||
if ((forceDir.X < 0) != (user.AnimController.Dir < 0))
|
||||
{
|
||||
forceDir.X = Math.Clamp(forceDir.X, -0.1f, 0.1f);
|
||||
// Prevents rubberbanding horizontally when dragging a corpse.
|
||||
lerpForces = true;
|
||||
}
|
||||
}
|
||||
float force = LerpForces ? MathHelper.Lerp(0, TargetPullForce, MathUtils.InverseLerp(0, MaxLength / 3, distance - 50)) : TargetPullForce;
|
||||
float force = lerpForces ? MathHelper.Lerp(0, TargetPullForce, MathUtils.InverseLerp(0, MaxLength / 3, distance - 50)) : TargetPullForce;
|
||||
targetBody?.ApplyForce(-forceDir * force);
|
||||
var targetRagdoll = targetCharacter?.AnimController;
|
||||
if (targetRagdoll != null && (targetRagdoll.InWater || targetRagdoll.OnGround))
|
||||
if (targetRagdoll?.Collider != null && (targetRagdoll.InWater || targetRagdoll.OnGround))
|
||||
{
|
||||
targetRagdoll.Collider.ApplyForce(-forceDir * force * 3);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace Barotrauma.Items.Components
|
||||
private readonly Item item;
|
||||
|
||||
public readonly bool IsOutput;
|
||||
|
||||
|
||||
public readonly List<StatusEffect> Effects;
|
||||
|
||||
public readonly List<(ushort wireId, int? connectionIndex)> LoadedWires;
|
||||
@@ -40,6 +40,9 @@ namespace Barotrauma.Items.Components
|
||||
//Priority in which power output will be handled - load is unaffected
|
||||
public PowerPriority Priority = PowerPriority.Default;
|
||||
|
||||
public Signal LastSentSignal { get; private set; }
|
||||
public Signal LastReceivedSignal {get; private set;}
|
||||
|
||||
public bool IsPower
|
||||
{
|
||||
get;
|
||||
@@ -292,6 +295,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public void SendSignal(Signal signal)
|
||||
{
|
||||
LastSentSignal = signal;
|
||||
enumeratingWires = true;
|
||||
foreach (var wire in wires)
|
||||
{
|
||||
@@ -302,6 +306,10 @@ namespace Barotrauma.Items.Components
|
||||
signal.source?.LastSentSignalRecipients.Add(recipient);
|
||||
|
||||
Connection connection = recipient;
|
||||
connection.LastReceivedSignal = signal;
|
||||
#if CLIENT
|
||||
wire.RegisterSignal(signal, source: this);
|
||||
#endif
|
||||
|
||||
foreach (ItemComponent ic in recipient.item.Components)
|
||||
{
|
||||
|
||||
@@ -82,8 +82,10 @@ namespace Barotrauma.Items.Components
|
||||
signalOut.SendDuration -= 1;
|
||||
item.SendSignal(new Signal(signalOut.Signal.value, sender: signalOut.Signal.sender, strength: signalOut.Signal.strength), "signal_out");
|
||||
if (signalOut.SendDuration <= 0)
|
||||
{
|
||||
signalQueue.Dequeue();
|
||||
{
|
||||
//check the queue isn't empty again, because sending the signal may empty it
|
||||
//if this component is set to reset when it receives a signal and the signal is routed back to this component
|
||||
signalQueue.TryDequeue(out _);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -173,6 +173,8 @@ namespace Barotrauma.Items.Components
|
||||
set
|
||||
{
|
||||
lightColor = value;
|
||||
//reset previously received signal to force updating the color if we receive a set_color signal after the color has been modified manually
|
||||
prevColorSignal = string.Empty;
|
||||
#if CLIENT
|
||||
if (Light != null)
|
||||
{
|
||||
@@ -249,6 +251,11 @@ namespace Barotrauma.Items.Components
|
||||
base.OnItemLoaded();
|
||||
SetLightSourceState(IsActive, lightBrightness);
|
||||
turret = item.GetComponent<Turret>();
|
||||
if (item.body != null)
|
||||
{
|
||||
item.body.FarseerBody.OnEnabled += CheckIfNeedsUpdate;
|
||||
item.body.FarseerBody.OnDisabled += CheckIfNeedsUpdate;
|
||||
}
|
||||
#if CLIENT
|
||||
Drawable = AlphaBlend && Light.LightSprite != null;
|
||||
if (Screen.Selected.IsEditor)
|
||||
@@ -277,15 +284,24 @@ namespace Barotrauma.Items.Components
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.body == null && powerConsumption <= 0.0f && Parent == null && turret == null &&
|
||||
if ((item.body == null || !item.body.Enabled) &&
|
||||
powerConsumption <= 0.0f && Parent == null && turret == null &&
|
||||
(statusEffectLists == null || !statusEffectLists.ContainsKey(ActionType.OnActive)) &&
|
||||
(IsActiveConditionals == null || IsActiveConditionals.Count == 0))
|
||||
{
|
||||
lightBrightness = 1.0f;
|
||||
SetLightSourceState(true, lightBrightness);
|
||||
if (item.body != null && !item.body.Enabled)
|
||||
{
|
||||
lightBrightness = 0.0f;
|
||||
SetLightSourceState(false, 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
lightBrightness = 1.0f;
|
||||
SetLightSourceState(true, lightBrightness);
|
||||
}
|
||||
isOn = true;
|
||||
SetLightSourceTransformProjSpecific();
|
||||
base.IsActive = false;
|
||||
isOn = true;
|
||||
#if CLIENT
|
||||
Light.ParentSub = item.Submarine;
|
||||
#endif
|
||||
|
||||
@@ -222,10 +222,10 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
Vector2 e1 = edge.Point1 + cell.Translation;
|
||||
Vector2 e2 = edge.Point2 + cell.Translation;
|
||||
if (MathUtils.LinesIntersect(e1, e2, new Vector2(detectRect.X, detectRect.Y), new Vector2(detectRect.Right, detectRect.Y)) ||
|
||||
MathUtils.LinesIntersect(e1, e2, new Vector2(detectRect.X, detectRect.Bottom), new Vector2(detectRect.Right, detectRect.Bottom)) ||
|
||||
MathUtils.LinesIntersect(e1, e2, new Vector2(detectRect.X, detectRect.Y), new Vector2(detectRect.X, detectRect.Bottom)) ||
|
||||
MathUtils.LinesIntersect(e1, e2, new Vector2(detectRect.Right, detectRect.Y), new Vector2(detectRect.Right, detectRect.Bottom)))
|
||||
if (MathUtils.LineSegmentsIntersect(e1, e2, new Vector2(detectRect.X, detectRect.Y), new Vector2(detectRect.Right, detectRect.Y)) ||
|
||||
MathUtils.LineSegmentsIntersect(e1, e2, new Vector2(detectRect.X, detectRect.Bottom), new Vector2(detectRect.Right, detectRect.Bottom)) ||
|
||||
MathUtils.LineSegmentsIntersect(e1, e2, new Vector2(detectRect.X, detectRect.Y), new Vector2(detectRect.X, detectRect.Bottom)) ||
|
||||
MathUtils.LineSegmentsIntersect(e1, e2, new Vector2(detectRect.Right, detectRect.Y), new Vector2(detectRect.Right, detectRect.Bottom)))
|
||||
{
|
||||
MotionDetected = true;
|
||||
return;
|
||||
|
||||
@@ -8,6 +8,9 @@ namespace Barotrauma.Items.Components
|
||||
internal Item source;
|
||||
internal float power;
|
||||
internal float strength;
|
||||
public readonly double CreationTime;
|
||||
|
||||
public double TimeSinceCreated => Timing.TotalTimeUnpaused - CreationTime;
|
||||
|
||||
internal Signal(string value, int stepsTaken = 0, Character sender = null,
|
||||
Item source = null, float power = 0.0f, float strength = 1.0f)
|
||||
@@ -18,6 +21,7 @@ namespace Barotrauma.Items.Components
|
||||
this.source = source;
|
||||
this.power = power;
|
||||
this.strength = strength;
|
||||
CreationTime = Timing.TotalTimeUnpaused;
|
||||
}
|
||||
|
||||
internal Signal WithStepsTaken(int stepsTaken)
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[ConditionallyEditable(ConditionallyEditable.ConditionType.AllowLinkingWifiToChat)]
|
||||
[ConditionallyEditable(ConditionallyEditable.ConditionType.AllowLinkingWifiToChat, onlyInEditors: false)]
|
||||
[Serialize(false, IsPropertySaveable.No, 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.", alwaysUseInstanceValues: true)]
|
||||
public bool LinkToChat
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Barotrauma.Items.Components
|
||||
private Vector2 end;
|
||||
|
||||
private readonly float angle;
|
||||
private readonly float length;
|
||||
public readonly float Length;
|
||||
|
||||
public Vector2 Start
|
||||
{
|
||||
@@ -36,7 +36,7 @@ namespace Barotrauma.Items.Components
|
||||
this.end = end;
|
||||
|
||||
angle = MathUtils.VectorToAngle(end - start);
|
||||
length = Vector2.Distance(start, end);
|
||||
Length = Vector2.Distance(start, end);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace Barotrauma.Items.Components
|
||||
private List<Vector2> nodes;
|
||||
private readonly List<WireSection> sections;
|
||||
|
||||
private Connection[] connections;
|
||||
private readonly Connection[] connections;
|
||||
|
||||
private bool canPlaceNode;
|
||||
private Vector2 newNodePos;
|
||||
@@ -81,6 +81,8 @@ namespace Barotrauma.Items.Components
|
||||
get { return connections; }
|
||||
}
|
||||
|
||||
public float Length { get; private set; }
|
||||
|
||||
[Serialize(5000.0f, IsPropertySaveable.No, description: "The maximum distance the wire can extend (in pixels).")]
|
||||
public float MaxLength
|
||||
{
|
||||
@@ -333,7 +335,7 @@ namespace Barotrauma.Items.Components
|
||||
IsActive = false;
|
||||
}
|
||||
|
||||
public override void Drop(Character dropper)
|
||||
public override void Drop(Character dropper, bool setTransform = true)
|
||||
{
|
||||
if (shouldClearConnections) { ClearConnections(dropper); }
|
||||
IsActive = false;
|
||||
@@ -563,6 +565,7 @@ namespace Barotrauma.Items.Components
|
||||
sections.Add(new WireSection(nodes[i], nodes[i + 1]));
|
||||
}
|
||||
Drawable = IsActive || sections.Count > 0;
|
||||
Length = sections.Count > 0 ? sections.Sum(s => s.Length) : 0;
|
||||
CalculateExtents();
|
||||
}
|
||||
|
||||
|
||||
@@ -56,11 +56,9 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private float resetUserTimer;
|
||||
|
||||
private float aiTargetingGraceTimer;
|
||||
|
||||
private float aiFindTargetTimer;
|
||||
private ISpatialEntity currentTarget;
|
||||
private const float CrewAiFindTargetMaxInterval = 3.0f;
|
||||
private const float CrewAiFindTargetMaxInterval = 1.0f;
|
||||
private const float CrewAIFindTargetMinInverval = 0.2f;
|
||||
|
||||
private int currentLoaderIndex;
|
||||
@@ -299,7 +297,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize(3000.0f, IsPropertySaveable.Yes, description: "How close to a target the turret has to be for an AI character to fire it.")]
|
||||
[Serialize(3500.0f, IsPropertySaveable.Yes, description: "How close to a target the turret has to be for an AI character to fire it.")]
|
||||
public float AIRange
|
||||
{
|
||||
get;
|
||||
@@ -422,10 +420,7 @@ namespace Barotrauma.Items.Components
|
||||
// Only make the Turret control the LightComponents that are it's children. So it'd be possible to for example have some extra lights on the turret that don't rotate with it.
|
||||
if (lc?.Parent == this)
|
||||
{
|
||||
if (lightComponents == null)
|
||||
{
|
||||
lightComponents = new List<LightComponent>();
|
||||
}
|
||||
lightComponents ??= new List<LightComponent>();
|
||||
lightComponents.Add(lc);
|
||||
}
|
||||
}
|
||||
@@ -439,6 +434,8 @@ namespace Barotrauma.Items.Components
|
||||
light.Parent = null;
|
||||
light.Rotation = Rotation - item.RotationRad;
|
||||
light.Light.Rotation = -rotation;
|
||||
//turret lights are high-prio (don't want the lights to disappear when you're fighting something)
|
||||
light.Light.PriorityMultiplier *= 10.0f;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -590,10 +587,6 @@ namespace Barotrauma.Items.Components
|
||||
angularVelocity *= -0.5f;
|
||||
}
|
||||
|
||||
if (aiTargetingGraceTimer > 0f)
|
||||
{
|
||||
aiTargetingGraceTimer -= deltaTime;
|
||||
}
|
||||
if (aiFindTargetTimer > 0.0f)
|
||||
{
|
||||
aiFindTargetTimer -= deltaTime;
|
||||
@@ -620,10 +613,18 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime);
|
||||
|
||||
private bool isUseBeingCalled;
|
||||
|
||||
public override bool Use(float deltaTime, Character character = null)
|
||||
{
|
||||
if (!characterUsable && character != null) { return false; }
|
||||
return TryLaunch(deltaTime, character);
|
||||
//prevent an infinite loop if launching triggers a StatusEffect that Uses this item
|
||||
if (isUseBeingCalled) { return false; }
|
||||
|
||||
isUseBeingCalled = true;
|
||||
bool wasSuccessful = TryLaunch(deltaTime, character);
|
||||
isUseBeingCalled = false;
|
||||
return wasSuccessful;
|
||||
}
|
||||
|
||||
public float GetPowerRequiredToShoot()
|
||||
@@ -844,8 +845,13 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
public readonly Item Projectile;
|
||||
|
||||
public EventData(Item projectile)
|
||||
public EventData(Item projectile, Turret turret)
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(projectile != null, $"Tried to create Turret {nameof(EventData)} with no projectile.");
|
||||
GameAnalyticsManager.AddErrorEventOnce(
|
||||
"Turret.EventData:entitynull"+ turret.Item.Prefab.Identifier,
|
||||
GameAnalyticsManager.ErrorSeverity.Error,
|
||||
$"Turret \"{turret.Item.Prefab.Identifier}\" tried to create {nameof(EventData)} with no projectile.");
|
||||
Projectile = projectile;
|
||||
}
|
||||
}
|
||||
@@ -885,21 +891,9 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
float spread = MathHelper.ToRadians(Spread) * Rand.Range(-0.5f, 0.5f);
|
||||
|
||||
Vector2 launchPos = ConvertUnits.ToSimUnits(GetRelativeFiringPosition());
|
||||
|
||||
//check if there's some other sub between the turret's origin and the launch pos,
|
||||
//and if so, launch at the intersection of the turret and the sub to prevent the projectile from spawning inside the other sub
|
||||
Body pickedBody = Submarine.PickBody(ConvertUnits.ToSimUnits(item.WorldPosition), launchPos, null, Physics.CollisionWall, allowInsideFixture: true,
|
||||
customPredicate: (Fixture f) =>
|
||||
{
|
||||
return f.Body.UserData is not Submarine sub || sub != item.Submarine;
|
||||
});
|
||||
if (pickedBody != null)
|
||||
{
|
||||
launchPos = Submarine.LastPickedPosition;
|
||||
}
|
||||
projectile.SetTransform(launchPos, -(launchRotation ?? rotation) + spread);
|
||||
projectile.SetTransform(
|
||||
ConvertUnits.ToSimUnits(GetRelativeFiringPosition()),
|
||||
-(launchRotation ?? rotation) + spread);
|
||||
projectile.UpdateTransform();
|
||||
projectile.Submarine = projectile.body?.Submarine;
|
||||
|
||||
@@ -929,7 +923,7 @@ namespace Barotrauma.Items.Components
|
||||
projectile.Container?.RemoveContained(projectile);
|
||||
}
|
||||
#if SERVER
|
||||
item.CreateServerEvent(this, new EventData(projectile));
|
||||
item.CreateServerEvent(this, new EventData(projectile, this));
|
||||
#endif
|
||||
|
||||
ApplyStatusEffects(ActionType.OnUse, 1.0f, user: user);
|
||||
@@ -1027,6 +1021,7 @@ namespace Barotrauma.Items.Components
|
||||
float dist = Vector2.DistanceSquared(item.WorldPosition, targetItem.WorldPosition);
|
||||
if (dist > closestDist) { continue; }
|
||||
if (dist > shootDistance * shootDistance) { continue; }
|
||||
if (!IsTargetItemCloseEnough(targetItem, dist)) { continue; }
|
||||
if (!CheckTurretAngle(targetItem.WorldPosition)) { continue; }
|
||||
target = targetItem;
|
||||
closestDist = dist / priority;
|
||||
@@ -1142,9 +1137,12 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (character.AIController.SelectedAiTarget?.Entity is Character previousTarget && previousTarget.IsDead)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogTurretTargetDead").Value,
|
||||
identifier: $"killedtarget{previousTarget.ID}".ToIdentifier(),
|
||||
minDurationBetweenSimilar: 10.0f);
|
||||
if (previousTarget.LastAttacker == null || previousTarget.LastAttacker == character)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogTurretTargetDead").Value,
|
||||
identifier: $"killedtarget{previousTarget.ID}".ToIdentifier(),
|
||||
minDurationBetweenSimilar: 5.0f);
|
||||
}
|
||||
character.AIController.SelectTarget(null);
|
||||
}
|
||||
|
||||
@@ -1277,18 +1275,27 @@ namespace Barotrauma.Items.Components
|
||||
Vector2? targetPos = null;
|
||||
float maxDistance = 10000;
|
||||
float shootDistance = AIRange * item.OffsetOnSelectedMultiplier;
|
||||
// use full range only if we're actively firing
|
||||
if (aiTargetingGraceTimer <= 0f)
|
||||
{
|
||||
shootDistance *= 0.75f;
|
||||
}
|
||||
|
||||
float closestDistance = maxDistance * maxDistance;
|
||||
|
||||
bool hadCurrentTarget = currentTarget != null;
|
||||
if (hadCurrentTarget)
|
||||
{
|
||||
if (!IsValidTarget(currentTarget))
|
||||
bool isValidTarget = IsValidTarget(currentTarget);
|
||||
if (isValidTarget)
|
||||
{
|
||||
float dist = Vector2.DistanceSquared(item.WorldPosition, currentTarget.WorldPosition);
|
||||
if (dist > closestDistance)
|
||||
{
|
||||
isValidTarget = false;
|
||||
}
|
||||
else if (currentTarget is Item targetItem)
|
||||
{
|
||||
if (!IsTargetItemCloseEnough(targetItem, dist))
|
||||
{
|
||||
isValidTarget = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isValidTarget)
|
||||
{
|
||||
currentTarget = null;
|
||||
aiFindTargetTimer = CrewAIFindTargetMinInverval;
|
||||
@@ -1304,11 +1311,17 @@ namespace Barotrauma.Items.Components
|
||||
if (character.Submarine != null)
|
||||
{
|
||||
if (enemy.Submarine == character.Submarine) { continue; }
|
||||
if (enemy.Submarine != null && enemy.Submarine.TeamID == character.Submarine.TeamID) { continue; }
|
||||
if (enemy.Submarine != null)
|
||||
{
|
||||
if (enemy.Submarine.TeamID == character.Submarine.TeamID) { continue; }
|
||||
if (enemy.Submarine.Info.IsOutpost) { continue; }
|
||||
}
|
||||
}
|
||||
// Don't aim monsters that are inside any submarine.
|
||||
if (!enemy.IsHuman && enemy.CurrentHull != null) { continue; }
|
||||
if (HumanAIController.IsFriendly(character, enemy)) { continue; }
|
||||
if (HumanAIController.IsFriendly(character, enemy)) { continue; }
|
||||
// Don't shoot at captured enemies.
|
||||
if (enemy.LockHands) { continue; }
|
||||
float dist = Vector2.DistanceSquared(enemy.WorldPosition, item.WorldPosition);
|
||||
if (dist > closestDistance) { continue; }
|
||||
if (dist < shootDistance * shootDistance)
|
||||
@@ -1330,6 +1343,7 @@ namespace Barotrauma.Items.Components
|
||||
float dist = Vector2.DistanceSquared(item.WorldPosition, targetItem.WorldPosition);
|
||||
if (dist > closestDistance) { continue; }
|
||||
if (dist > shootDistance * shootDistance) { continue; }
|
||||
if (!IsTargetItemCloseEnough(targetItem, dist)) { continue; }
|
||||
if (!CheckTurretAngle(targetItem.WorldPosition)) { continue; }
|
||||
targetPos = targetItem.WorldPosition;
|
||||
closestDistance = dist / priority;
|
||||
@@ -1337,14 +1351,7 @@ namespace Barotrauma.Items.Components
|
||||
closestEnemy = null;
|
||||
currentTarget = targetItem;
|
||||
}
|
||||
if (currentTarget == null)
|
||||
{
|
||||
aiFindTargetTimer = CrewAIFindTargetMinInverval;
|
||||
}
|
||||
else
|
||||
{
|
||||
aiFindTargetTimer = CrewAiFindTargetMaxInterval;
|
||||
}
|
||||
aiFindTargetTimer = currentTarget == null ? CrewAiFindTargetMaxInterval : CrewAIFindTargetMinInverval;
|
||||
}
|
||||
else if (currentTarget != null)
|
||||
{
|
||||
@@ -1358,6 +1365,10 @@ namespace Barotrauma.Items.Components
|
||||
if (targetCharacter.Submarine != null && targetCharacter.CurrentHull != null && targetCharacter.Submarine != item.Submarine && !targetCharacter.CanSeeTarget(Item))
|
||||
{
|
||||
targetPos = targetCharacter.CurrentHull.WorldPosition;
|
||||
if (closestDistance > maxDistance * maxDistance)
|
||||
{
|
||||
ResetTarget();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1377,12 +1388,17 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
if (closestDist > shootDistance * shootDistance)
|
||||
{
|
||||
// Not close enough to shoot.
|
||||
currentTarget = null;
|
||||
closestEnemy = null;
|
||||
targetPos = null;
|
||||
aiFindTargetTimer = CrewAIFindTargetMinInverval;
|
||||
ResetTarget();
|
||||
}
|
||||
}
|
||||
void ResetTarget()
|
||||
{
|
||||
// Not close enough to shoot.
|
||||
currentTarget = null;
|
||||
closestEnemy = null;
|
||||
targetPos = null;
|
||||
}
|
||||
}
|
||||
else if (targetPos == null && item.Submarine != null && Level.Loaded != null)
|
||||
{
|
||||
@@ -1404,7 +1420,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
// The closest point can't be targeted -> get a point directly in front of the turret
|
||||
Vector2 barrelDir = new Vector2((float)Math.Cos(rotation), -(float)Math.Sin(rotation));
|
||||
if (MathUtils.GetLineIntersection(p1, p2, item.WorldPosition, item.WorldPosition + barrelDir * shootDistance, out Vector2 intersection))
|
||||
if (MathUtils.GetLineSegmentIntersection(p1, p2, item.WorldPosition, item.WorldPosition + barrelDir * shootDistance, out Vector2 intersection))
|
||||
{
|
||||
closestPoint = intersection;
|
||||
if (!CheckTurretAngle(closestPoint)) { continue; }
|
||||
@@ -1532,10 +1548,11 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
character.SetInput(InputType.Shoot, true, true);
|
||||
}
|
||||
aiTargetingGraceTimer = 5f;
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool IsTargetItemCloseEnough(Item target, float sqrDist) => float.IsPositiveInfinity(target.Prefab.AITurretTargetingMaxDistance) || sqrDist < MathUtils.Pow2(target.Prefab.AITurretTargetingMaxDistance);
|
||||
|
||||
/// <summary>
|
||||
/// Turret doesn't consume grid power, directly takes from the batteries on its grid instead.
|
||||
/// </summary>
|
||||
@@ -1565,6 +1582,10 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (targetItem.ParentInventory != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1580,6 +1601,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (item.Submarine != null)
|
||||
{
|
||||
if (item.Submarine.Info.IsOutpost) { return false; }
|
||||
// Check that the target is not in the friendly team, e.g. pirate or a hostile player sub (PvP).
|
||||
return !target.IsOnFriendlyTeam(item.Submarine.TeamID) && TargetHumans;
|
||||
}
|
||||
@@ -1672,7 +1694,7 @@ namespace Barotrauma.Items.Components
|
||||
return angle >= minRotation && angle <= maxRotation;
|
||||
}
|
||||
|
||||
private bool CheckTurretAngle(Vector2 target) => CheckTurretAngle(-MathUtils.VectorToAngle(target - item.WorldPosition));
|
||||
public bool CheckTurretAngle(Vector2 target) => CheckTurretAngle(-MathUtils.VectorToAngle(target - item.WorldPosition));
|
||||
|
||||
protected override void RemoveComponentSpecific()
|
||||
{
|
||||
@@ -1874,22 +1896,27 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (TryExtractEventData(extraData, out EventData eventData))
|
||||
{
|
||||
msg.WriteUInt16(eventData.Projectile.ID);
|
||||
msg.WriteRangedSingle(MathHelper.Clamp(rotation, minRotation, maxRotation), minRotation, maxRotation, 16);
|
||||
msg.WriteUInt16(eventData.Projectile?.ID ?? Entity.NullEntityID);
|
||||
msg.WriteRangedSingle(MathHelper.Clamp(wrapAngle(rotation), minRotation, maxRotation), minRotation, maxRotation, 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.WriteUInt16((ushort)0);
|
||||
float wrappedTargetRotation = targetRotation;
|
||||
while (wrappedTargetRotation < minRotation && MathUtils.IsValid(wrappedTargetRotation))
|
||||
msg.WriteRangedSingle(MathHelper.Clamp(wrapAngle(targetRotation), minRotation, maxRotation), minRotation, maxRotation, 16);
|
||||
}
|
||||
|
||||
float wrapAngle(float angle)
|
||||
{
|
||||
float wrappedAngle = angle;
|
||||
while (wrappedAngle < minRotation && MathUtils.IsValid(wrappedAngle))
|
||||
{
|
||||
wrappedTargetRotation += MathHelper.TwoPi;
|
||||
wrappedAngle += MathHelper.TwoPi;
|
||||
}
|
||||
while (wrappedTargetRotation > maxRotation && MathUtils.IsValid(wrappedTargetRotation))
|
||||
while (wrappedAngle > maxRotation && MathUtils.IsValid(wrappedAngle))
|
||||
{
|
||||
wrappedTargetRotation -= MathHelper.TwoPi;
|
||||
wrappedAngle -= MathHelper.TwoPi;
|
||||
}
|
||||
msg.WriteRangedSingle(MathHelper.Clamp(wrappedTargetRotation, minRotation, maxRotation), minRotation, maxRotation, 16);
|
||||
return wrappedAngle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -482,11 +482,11 @@ namespace Barotrauma.Items.Components
|
||||
character.OnWearablesChanged();
|
||||
}
|
||||
|
||||
public override void Drop(Character dropper)
|
||||
public override void Drop(Character dropper, bool setTransform = true)
|
||||
{
|
||||
Character previousPicker = picker;
|
||||
Unequip(picker);
|
||||
base.Drop(dropper);
|
||||
base.Drop(dropper, setTransform);
|
||||
previousPicker?.OnWearablesChanged();
|
||||
picker = null;
|
||||
IsActive = false;
|
||||
|
||||
Reference in New Issue
Block a user