4 using FarseerPhysics.Dynamics;
5 using FarseerPhysics.Dynamics.Joints;
6 using Microsoft.Xna.Framework;
8 using System.Collections.Generic;
27 private static readonly List<DockingPort> list =
new List<DockingPort>();
28 public static IEnumerable<DockingPort>
List
33 private Sprite overlaySprite;
34 private float dockingState;
37 private readonly
Hull[] hulls =
new Hull[2];
39 private Body[] bodies;
40 private Fixture outsideBlocker;
41 private Body doorBody;
43 private float dockingCooldown;
46 private bool obstructedWayPointsDisabled;
48 private float forceLockTimer;
51 const float ForceLockDelay = 1.0f;
72 [
Editable,
Serialize(
false,
IsPropertySaveable.Yes, description:
"If set to true, this docking port is used when spawning the submarine docked to an outpost (if possible).")]
79 [
Editable,
Serialize(
true,
IsPropertySaveable.No, description:
"Should the OnUse StatusEffects trigger when docking (on vanilla docking ports these effects emit particles and play a sound).)")]
87 "Normally there's no need to touch this setting, but if you notice the docking position is incorrect (for example due to some unusual docking port configuration without hulls or doors), you can use this to enforce the direction.")]
108 if (!docked && value)
115 else if (docked && !value)
124 get {
return joint is WeldJoint ||
DockingTarget?.joint is WeldJoint; }
139 private bool outpostAutoDockingPromptShown;
141 enum AllowOutpostAutoDocking
145 private AllowOutpostAutoDocking allowOutpostAutoDocking = AllowOutpostAutoDocking.Ask;
148 : base(
item, element)
151 foreach (var subElement
in element.Elements())
153 string texturePath = subElement.GetAttributeString(
"texture",
"");
154 switch (subElement.Name.ToString().ToLowerInvariant())
157 overlaySprite =
new Sprite(subElement, texturePath.Contains(
"/") ?
"" : Path.GetDirectoryName(
item.
Prefab.
FilePath));
167 public override void FlipX(
bool relativeToSub)
180 Undock(applyEffects:
false);
181 Dock(prevDockingTarget);
182 Lock(isNetworkMessage:
true, applyEffects:
false);
186 public override void FlipY(
bool relativeToSub)
188 FlipX(relativeToSub);
193 float closestDist =
float.MaxValue;
203 float dist = xDist + yDist;
206 if (dist < closestDist)
215 private void AttemptDock()
217 var adjacentPort = FindAdjacentPort();
218 if (adjacentPort !=
null) {
Dock(adjacentPort); }
225 forceLockTimer = 0.0f;
226 dockingCooldown = 0.1f;
235 DebugConsole.ThrowError(
"Error - tried to dock a submarine to itself");
240 target.InitializeLinks();
276 item.CreateServerEvent(
this);
284 public void Lock(
bool isNetworkMessage,
bool applyEffects =
true)
292 DebugConsole.ThrowError(
"Error - attempted to lock a docking port that's not connected to anything");
298 string errorMsg =
"Error while locking a docking port (joint between submarines doesn't exist)." +
301 GameAnalyticsManager.AddErrorEventOnce(
"DockingPort.Lock:JointNotCreated", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
305 if (joint is not WeldJoint)
315 Vector2 jointDiff = joint.WorldAnchorB - joint.WorldAnchorA;
327 ConnectWireBetweenPorts();
335 item.CreateServerEvent(
this);
347 List<MapEntity> removedEntities =
item.
linkedTo.Where(e => e.Removed).ToList();
360 if (myWayPoint !=
null && targetWayPoint !=
null)
370 private void CreateJoint(
bool useWeldJoint)
390 ConvertUnits.ToSimUnits(pos1), ConvertUnits.ToSimUnits(pos2),
true);
392 ((WeldJoint)joint).FrequencyHz = 1.0f;
393 joint.CollideConnected =
false;
397 var distanceJoint = JointFactory.CreateDistanceJoint(
GameMain.
World,
399 ConvertUnits.ToSimUnits(pos1), ConvertUnits.ToSimUnits(pos2),
true);
401 distanceJoint.Length = 0.01f;
402 distanceJoint.Frequency = 1.0f;
403 distanceJoint.DampingRatio = 0.8f;
405 joint = distanceJoint;
406 joint.CollideConnected =
true;
412 int forcedDockingDir = GetForcedDockingDir();
413 if (forcedDockingDir != 0) {
return forcedDockingDir; }
414 if (dockingTarget !=
null)
416 forcedDockingDir = -dockingTarget.GetForcedDockingDir();
417 if (forcedDockingDir != 0) {
return forcedDockingDir; }
425 float largestHullSize = 0.0f;
428 if (!(linked is
Hull hull)) {
continue; }
429 if (hull.Volume > largestHullSize)
432 largestHullSize = hull.
Volume;
442 if (dockingTarget?.
Door?.LinkedGap !=
null && dockingTarget.
Door.LinkedGap.linkedTo.Count > 0)
445 float largestHullSize = 0.0f;
448 if (!(linked is
Hull hull)) {
continue; }
449 if (hull.Volume > largestHullSize)
452 largestHullSize = hull.
Volume;
458 Math.Sign(refHull.WorldPosition.X - dockingTarget.Door.Item.WorldPosition.X) :
459 Math.Sign(refHull.WorldPosition.Y - dockingTarget.Door.Item.WorldPosition.Y);
462 if (dockingTarget !=
null)
467 if (dir != 0) {
return dir; }
479 private int GetForcedDockingDir()
495 private void ConnectWireBetweenPorts()
497 Wire wire =
item.GetComponent<Wire>();
498 if (wire ==
null) {
return; }
506 if (powerConnection ==
null) {
return; }
510 if (recipient ==
null) {
return; }
512 wire.RemoveConnection(
item);
515 powerConnection.TryAddLink(wire);
516 wire.TryConnect(powerConnection, addNode:
false);
517 recipient.TryAddLink(wire);
518 wire.TryConnect(recipient, addNode:
false);
521 Powered.ChangedConnections.Add(powerConnection);
522 Powered.ChangedConnections.Add(recipient);
525 private void CreateDoorBody()
527 if (doorBody !=
null)
529 GameMain.World.Remove(doorBody);
534 if (!MathUtils.IsValid(position))
537 "Attempted to create a door body at an invalid position (item pos: " +
item.
Position
541 DebugConsole.ThrowError(errorMsg);
542 GameAnalyticsManager.AddErrorEventOnce(
543 "DockingPort.CreateDoorBody:InvalidPosition",
544 GameAnalyticsManager.ErrorSeverity.Error,
546 position = Vector2.Zero;
549 System.Diagnostics.Debug.Assert(doorBody ==
null);
551 doorBody = GameMain.World.CreateRectangle(
557 doorBody.CollisionCategories = Physics.CollisionWall;
558 doorBody.BodyType = BodyType.Static;
561 private void CreateHulls()
566 bodies =
new Body[4];
581 if (hullRects[0].
Center.X > hullRects[1].Center.X)
588 hullRects[0] =
new Rectangle(hullRects[0].
Center.X, hullRects[0].Y, scaledDockedDistance, hullRects[0].Height);
589 hullRects[1] =
new Rectangle(hullRects[1].
Center.X - scaledDockedDistance, hullRects[1].Y, scaledDockedDistance, hullRects[1].Height);
592 int leftSubRightSide =
int.MinValue, rightSubLeftSide =
int.MaxValue;
593 foreach (Hull hull
in Hull.HullList)
595 for (
int i = 0; i < 2; i++)
597 if (hull.Submarine != subs[i]) {
continue; }
598 if (hull.WorldRect.Y - 5 < hullRects[i].Y - hullRects[i].Height) {
continue; }
599 if (hull.WorldRect.Y - hull.WorldRect.Height + 5 > hullRects[i].Y) {
continue; }
603 if (hull.WorldPosition.X > hullRects[0].Center.X) {
continue; }
604 leftSubRightSide = Math.Max(hull.WorldRect.Right, leftSubRightSide);
608 if (hull.WorldPosition.X < hullRects[1].Center.X) {
continue; }
609 rightSubLeftSide = Math.Min(hull.WorldRect.X, rightSubLeftSide);
614 if (leftSubRightSide ==
int.MinValue || rightSubLeftSide ==
int.MaxValue)
616 DebugConsole.NewMessage(
"Creating hulls between docking ports failed. Could not find a hull next to the docking port.");
623 int leftHullDiff = (hullRects[0].X - leftSubRightSide) + 5;
624 if (leftHullDiff > 0)
626 if (leftHullDiff > 100)
628 DebugConsole.NewMessage(
"Creating hulls between docking ports failed. The leftmost docking port seems to be very far from any hulls in the left-side submarine.");
633 hullRects[0].X -= leftHullDiff;
634 hullRects[0].Width += leftHullDiff;
638 int rightHullDiff = (rightSubLeftSide - hullRects[1].Right) + 5;
639 if (rightHullDiff > 0)
641 if (rightHullDiff > 100)
643 DebugConsole.NewMessage(
"Creating hulls between docking ports failed. The rightmost docking port seems to be very far from any hulls in the right-side submarine.");
648 hullRects[1].Width += rightHullDiff;
653 for (
int i = 0; i < 2; i++)
655 hullRects[i].X -= expand;
656 hullRects[i].Width += expand * 2;
657 hullRects[i].Location -= MathUtils.ToPoint(subs[i].WorldPosition - subs[i].HiddenSubPosition);
658 hulls[i] =
new Hull(hullRects[i], subs[i])
660 RoomName =
IsHorizontal ?
"entityname.dockingport" :
"entityname.dockinghatch",
664 hulls[i].AddToGrid(subs[i]);
667 for (
int j = 0; j < 2; j++)
669 bodies[i + j * 2] = GameMain.World.CreateEdge(
670 ConvertUnits.ToSimUnits(
new Vector2(hullRects[i].X, hullRects[i].Y - hullRects[i].Height * j)),
671 ConvertUnits.ToSimUnits(
new Vector2(hullRects[i].Right, hullRects[i].Y - hullRects[i].Height * j)),
676 for (
int i = 0; i < 2; i++)
685 if (rightHullDiff <= 100 && hulls[0].Submarine !=
null)
687 outsideBlocker = hulls[0].Submarine.PhysicsBody.FarseerBody.CreateRectangle(
688 ConvertUnits.ToSimUnits(hullRects[0].Width + hullRects[1].Width),
689 ConvertUnits.ToSimUnits(hullRects[0].Height),
691 offset: ConvertUnits.ToSimUnits(
new Vector2(hullRects[0].Right, hullRects[0].Y - hullRects[0].Height / 2) - hulls[0].
Submarine.HiddenSubPosition),
692 Physics.CollisionWall,
693 Physics.CollisionCharacter | Physics.CollisionItem | Physics.CollisionCharacter | Physics.CollisionItemBlocking | Physics.CollisionProjectile);
694 outsideBlocker.UserData =
this;
697 gap =
new Gap(
new Rectangle(hullRects[0].Right - 2, hullRects[0].Y, 4, hullRects[0].Height),
true, subs[0]);
701 if (hullRects[0].
Center.Y > hullRects[1].Center.Y)
708 hullRects[0] =
new Rectangle(hullRects[0].X, hullRects[0].Y - hullRects[0].Height / 2 + scaledDockedDistance, hullRects[0].Width, scaledDockedDistance);
709 hullRects[1] =
new Rectangle(hullRects[1].X, hullRects[1].Y - hullRects[1].Height / 2, hullRects[1].Width, scaledDockedDistance);
712 int upperSubBottom =
int.MaxValue, lowerSubTop =
int.MinValue;
713 foreach (Hull hull
in Hull.HullList)
715 for (
int i = 0; i < 2; i++)
717 if (hull.Submarine != subs[i]) {
continue; }
718 if (hull.WorldRect.Right - 5 < hullRects[i].X) {
continue; }
719 if (hull.WorldRect.X + 5 > hullRects[i].Right) {
continue; }
723 if (hull.WorldPosition.Y > hullRects[i].Y - hullRects[i].Height / 2) {
continue; }
724 lowerSubTop = Math.Max(hull.WorldRect.Y, lowerSubTop);
728 if (hull.WorldPosition.Y < hullRects[i].Y - hullRects[i].Height / 2) {
continue; }
729 upperSubBottom = Math.Min(hull.WorldRect.Y - hull.WorldRect.Height, upperSubBottom);
734 if (upperSubBottom ==
int.MaxValue || lowerSubTop ==
int.MinValue)
736 DebugConsole.NewMessage(
"Creating hulls between docking ports failed. Could not find a hull next to the docking port.");
743 int lowerHullDiff = ((hullRects[0].Y - hullRects[0].Height) - lowerSubTop) + 5;
744 if (lowerHullDiff > 0)
746 if (lowerHullDiff > 100)
748 DebugConsole.NewMessage(
"Creating hulls between docking ports failed. The lower docking port seems to be very far from any hulls in the lower submarine.");
753 hullRects[0].Height += lowerHullDiff;
757 int upperHullDiff = (upperSubBottom - hullRects[1].Y) + 5;
758 if (upperHullDiff > 0)
760 if (upperHullDiff > 100)
762 DebugConsole.NewMessage(
"Creating hulls between docking ports failed. The upper docking port seems to be very far from any hulls in the upper submarine.");
767 hullRects[1].Y += upperHullDiff;
768 hullRects[1].Height += upperHullDiff;
774 int midHullDiff = ((hullRects[1].Y - hullRects[1].Height) - hullRects[0].Y) + 2;
775 if (midHullDiff > 100)
777 DebugConsole.NewMessage(
"Creating hulls between docking ports failed. The upper hull seems to be very far from the lower hull.");
780 else if (midHullDiff > 0)
782 hullRects[0].Height += midHullDiff / 2 + 1;
783 hullRects[1].Y -= midHullDiff / 2 + 1;
784 hullRects[1].Height += midHullDiff / 2 + 1;
788 for (
int i = 0; i < 2; i++)
790 hullRects[i].Y += expand;
791 hullRects[i].Height += expand * 2;
792 hullRects[i].Location -= MathUtils.ToPoint(subs[i].WorldPosition - subs[i].HiddenSubPosition);
793 hulls[i] =
new Hull(hullRects[i], subs[i])
795 RoomName =
IsHorizontal ?
"entityname.dockingport" :
"entityname.dockinghatch",
798 hulls[i].AddToGrid(subs[i]);
801 for (
int j = 0; j < 2; j++)
803 bodies[i + j * 2] = GameMain.World.CreateEdge(
804 ConvertUnits.ToSimUnits(
new Vector2(hullRects[i].X + hullRects[i].Width * j, hullRects[i].Y)),
805 ConvertUnits.ToSimUnits(
new Vector2(hullRects[i].X + hullRects[i].Width * j, hullRects[i].Y - hullRects[i].Height)),
810 for (
int i = 0; i < 2; i++)
819 if (midHullDiff <= 100 && hulls[0].Submarine !=
null)
821 outsideBlocker = hulls[0].Submarine.PhysicsBody.FarseerBody.CreateRectangle(
822 ConvertUnits.ToSimUnits(hullRects[0].Width),
823 ConvertUnits.ToSimUnits(hullRects[0].Height + hullRects[1].Height),
825 offset: ConvertUnits.ToSimUnits(
new Vector2(hullRects[0].
Center.X, hullRects[0].Y) - hulls[0].Submarine.HiddenSubPosition),
826 Physics.CollisionWall,
827 Physics.CollisionCharacter | Physics.CollisionItem | Physics.CollisionCharacter | Physics.CollisionItemBlocking | Physics.CollisionProjectile);
828 outsideBlocker.UserData =
this;
831 gap =
new Gap(
new Rectangle(hullRects[0].X, hullRects[0].Y + 2, hullRects[0].Width, 4),
false, subs[0]);
838 hulls[0].ShouldBeSaved =
false;
839 hulls[1].ShouldBeSaved =
false;
848 foreach (Body body
in bodies)
850 if (body ==
null) {
continue; }
851 body.BodyType = BodyType.Static;
852 body.Friction = 0.5f;
856 partial
void RemoveConvexHulls();
858 private void LinkHullsToGaps()
860 if (gap ==
null || hulls ==
null || hulls[0] ==
null || hulls[1] ==
null)
863 DebugConsole.ThrowError(
"Failed to link dockingport hulls to gap");
872 if (hulls[0].WorldRect.X > hulls[1].WorldRect.X)
883 if (hulls[0].WorldRect.Y < hulls[1].WorldRect.Y)
893 for (
int i = 0; i < 2; i++)
896 if (doorGap ==
null) {
continue; }
898 if (doorGap.linkedTo.Count >= 2) {
continue; }
904 if (!doorGap.linkedTo.Contains(hulls[0])) { doorGap.linkedTo.Add(hulls[0]); }
908 if (!doorGap.linkedTo.Contains(hulls[1])) { doorGap.linkedTo.Add(hulls[1]); }
911 if (doorGap.linkedTo.Count > 1 && doorGap.linkedTo[0].WorldRect.X > doorGap.linkedTo[1].WorldRect.X)
913 var temp = doorGap.linkedTo[0];
914 doorGap.linkedTo[0] = doorGap.linkedTo[1];
915 doorGap.linkedTo[1] = temp;
922 if (!doorGap.linkedTo.Contains(hulls[0])) { doorGap.linkedTo.Add(hulls[0]); }
926 if (!doorGap.linkedTo.Contains(hulls[1])) { doorGap.linkedTo.Add(hulls[1]); }
929 if (doorGap.linkedTo.Count > 1 && doorGap.linkedTo[0].WorldRect.Y < doorGap.linkedTo[1].WorldRect.Y)
931 var temp = doorGap.linkedTo[0];
932 doorGap.linkedTo[0] = doorGap.linkedTo[1];
933 doorGap.linkedTo[1] = temp;
939 public void Undock(
bool applyEffects =
true)
943 forceLockTimer = 0.0f;
944 dockingCooldown = 0.1f;
961 if (myWayPoint !=
null && targetWayPoint !=
null)
964 if (myWayPoint.
linkedTo.Contains(targetWayPoint))
966 myWayPoint.
linkedTo.Remove(targetWayPoint);
970 if (targetWayPoint.
linkedTo.Contains(myWayPoint))
972 targetWayPoint.
linkedTo.Remove(myWayPoint);
985 obstructedWayPointsDisabled =
false;
992 if (powerConnection !=
null)
997 if (doorBody !=
null)
1003 var wire =
item.GetComponent<
Wire>();
1012 hulls[0]?.Remove(); hulls[0] =
null;
1013 hulls[1]?.Remove(); hulls[1] =
null;
1015 RemoveConvexHulls();
1025 foreach (Body body
in bodies)
1027 if (body ==
null) {
continue; }
1033 outsideBlocker?.Body.Remove(outsideBlocker);
1034 outsideBlocker =
null;
1039 item.CreateServerEvent(
this);
1042 autodockingVerification?.
Close();
1043 autodockingVerification =
null;
1051 dockingCooldown -= deltaTime;
1054 dockingState = MathHelper.Lerp(dockingState, 0.0f, deltaTime * 10.0f);
1055 if (dockingState < 0.01f) { docked =
false; }
1067 if (joint is DistanceJoint)
1069 dockingState = MathHelper.Lerp(dockingState, 0.5f, deltaTime * 10.0f);
1071 forceLockTimer += deltaTime;
1073 Vector2 jointDiff = joint.WorldAnchorB - joint.WorldAnchorA;
1075 if (jointDiff.LengthSquared() > 0.04f * 0.04f && forceLockTimer < ForceLockDelay)
1078 float massRatio1, massRatio2;
1096 Vector2 desiredRelativeVelocity = Vector2.Normalize(jointDiff);
1103 Lock(isNetworkMessage:
false);
1112 dockingState = MathHelper.Lerp(dockingState, 1.0f, deltaTime * 10.0f);
1117 if (!obstructedWayPointsDisabled && dockingState >= 0.99f)
1121 obstructedWayPointsDisabled =
true;
1127 base.RemoveComponentSpecific();
1129 hulls[0]?.Remove(); hulls[0] =
null;
1130 hulls[1]?.Remove(); hulls[1] =
null;
1131 gap?.
Remove(); gap =
null;
1132 RemoveConvexHulls();
1135 overlaySprite =
null;
1138 private bool initialized =
false;
1139 private void InitializeLinks()
1141 if (initialized) {
return; }
1150 var doorComponent = it.GetComponent<
Door>();
1151 if (doorComponent ==
null || doorComponent.IsHorizontal ==
IsHorizontal) {
continue; }
1157 yDist = Math.Min(closestYDist, yDist);
1165 if (yDist <= closestYDist)
1167 Door = doorComponent;
1168 closestYDist = yDist;
1174 List<MapEntity> linked =
new List<MapEntity>(
item.
linkedTo);
1175 foreach (MapEntity entity
in linked)
1177 if (entity is Hull hull)
1184 if (entity is Gap gap)
1209 List<MapEntity> linked =
new List<MapEntity>(
item.
linkedTo);
1212 if (!(entity is
Item linkedItem)) {
continue; }
1214 var dockingPort = linkedItem.GetComponent<
DockingPort>();
1215 if (dockingPort !=
null)
1235 if (dockingCooldown > 0.0f) {
return; }
1237 bool wasDocked = docked;
1240 bool newDockedState = wasDocked;
1241 switch (connection.
Name)
1244 if (signal.
value !=
"0")
1246 newDockedState = !docked;
1251 newDockedState = signal.
value !=
"0";
1255 if (newDockedState != wasDocked)
1257 bool tryingToToggleOutpostDocking = docked ?
1265 if (allowOutpostAutoDocking == AllowOutpostAutoDocking.Ask)
1268 if (!outpostAutoDockingPromptShown)
1271 TextManager.Get(newDockedState ?
"autodockverification" :
"autoundockverification"),
1272 new LocalizedString[] { TextManager.Get(
"Yes"), TextManager.Get(
"No") });
1273 autodockingVerification.
Buttons[0].OnClicked += (btn, userdata) =>
1275 autodockingVerification?.
Close();
1276 autodockingVerification =
null;
1278 allowOutpostAutoDocking = AllowOutpostAutoDocking.Yes;
1279 item.CreateClientEvent(
this);
1282 autodockingVerification.
Buttons[1].OnClicked += (btn, userdata) =>
1284 autodockingVerification?.
Close();
1285 autodockingVerification =
null;
1287 allowOutpostAutoDocking = AllowOutpostAutoDocking.No;
1288 item.CreateClientEvent(
this);
1293 outpostAutoDockingPromptShown =
true;
1296 else if (allowOutpostAutoDocking == AllowOutpostAutoDocking.No)
1308 if (signal.
sender !=
null && docked != wasDocked)
static bool AllowedToManageCampaign(ClientPermissions permissions)
There is a server-side implementation of the method in MultiPlayerCampaign
static Character? Controlled
virtual Vector2 WorldPosition
void FreeID()
Removes the entity from the entity dictionary and frees up the ID it was using.
List< GUIButton > Buttons
static GameSession?? GameSession
static GameScreen GameScreen
static NetworkMember NetworkMember
List< Connection > Connections
static void UpdateHulls()
goes through every item and re-checks which hull they are in
override Vector2? Position
static readonly List< Item > ItemList
void SendSignal(string signal, string connectionName)
override void Update(float deltaTime, Camera cam)
bool AtStartExit
Can be used by status effects
override void OnMapLoaded()
Called when all items have been loaded. Use to initialize connections between items.
static IEnumerable< DockingPort > List
DockingPort(Item item, ContentXElement element)
int GetDir(DockingPort dockingTarget=null)
DockingPort DockingTarget
void Undock(bool applyEffects=true)
override void RemoveComponentSpecific()
override void FlipY(bool relativeToSub)
void Lock(bool isNetworkMessage, bool applyEffects=true)
Vector2 DistanceTolerance
bool AnotherPortInProximity
override void FlipX(bool relativeToSub)
override void ReceiveSignal(Signal signal, Connection connection)
Action OnUnDocked
Automatically cleared after undocking -> no need to unregister
DirectionType ForceDockingDirection
bool ApplyEffectsOnDocking
Action OnDocked
Automatically cleared after docking -> no need to unregister
void Dock(DockingPort target)
Door(Item item, ContentXElement element)
The base class for components holding the different functionalities of the item
void ApplyStatusEffects(ActionType type, float deltaTime, Character character=null, Limb targetLimb=null, Entity useTarget=null, Character user=null, Vector2? worldPosition=null, float afflictionMultiplier=1.0f)
static readonly HashSet< Connection > ChangedConnections
override void Drop(Character dropper, bool setTransform=true)
a Character has dropped the item
readonly List< MapEntity > linkedTo
void SetPosition(Vector2 position)
readonly Dictionary< Submarine, DockingPort > ConnectedDockingPorts
override Vector2? WorldPosition
IEnumerable< Submarine > DockedTo
void RefreshOutdoorNodes()
void RefreshConnectedSubs()
void EnableObstructedWaypoints(Submarine otherSub)
Only affects temporarily disabled waypoints.
void DisableObstructedWayPoints()
Permanently disables obstructed waypoints obstructed by the level.
static List< WayPoint > WayPointList
Action< WayPoint > OnLinksChanged
void ConnectTo(WayPoint wayPoint2)
Interface for entities that the server can send events to the clients
ActionType
ActionTypes define when a StatusEffect is executed.