Files
LuaCsForBarotraumaEP/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/ConnectionPanel.cs
Eero 046483b9da Revert "OBT1.1.0 Merge branch 'dev_pte' into dev"
This reverts commit 177cf89756, reversing
changes made to 42ba733cd4.
2025-12-29 11:18:11 +08:00

430 lines
15 KiB
C#

using System;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
partial class ConnectionPanel : ItemComponent, IServerSerializable, IClientSerializable
{
const int MaxConnectionCount = 256;
public readonly List<Connection> Connections = new List<Connection>();
private Character user;
/// <summary>
/// Wires that have been disconnected from the panel, but not removed completely (visible at the bottom of the connection panel).
/// </summary>
public readonly HashSet<Wire> DisconnectedWires = new HashSet<Wire>();
private List<ushort> disconnectedWireIds;
/// <summary>
/// Allows rewiring the connection panel despite rewiring being disabled on a server
/// </summary>
public bool AlwaysAllowRewiring
{
get
{
if (item.Submarine == null) { return true; }
switch (item.Submarine.Info.Type)
{
case SubmarineType.Wreck:
case SubmarineType.BeaconStation:
case SubmarineType.EnemySubmarine:
case SubmarineType.Ruin:
return true;
}
return false;
}
}
[Editable, Serialize(false, IsPropertySaveable.Yes, description: "Locked connection panels cannot be rewired in-game.", alwaysUseInstanceValues: true)]
public bool Locked
{
get;
set;
}
public bool TemporarilyLocked
{
get { return Level.IsLoadedOutpost && (item.GetComponent<DockingPort>()?.Docked ?? false); }
}
//connection panels can't be deactivated externally (by signals or status effects)
public override bool IsActive
{
get { return base.IsActive; }
set { /*do nothing*/ }
}
public Character User
{
get { return user; }
}
public ConnectionPanel(Item item, ContentXElement element)
: base(item, element)
{
foreach (var subElement in element.Elements())
{
if (Connections.Count == MaxConnectionCount)
{
DebugConsole.ThrowError($"Too many connections in the item {item.Prefab.Identifier} (> {MaxConnectionCount}).");
break;
}
switch (subElement.Name.ToString())
{
case "input":
Connections.Add(new Connection(subElement, connectionIndex: Connections.Count, this, IdRemap.DiscardId, isItemSwap: false));
break;
case "output":
Connections.Add(new Connection(subElement, connectionIndex: Connections.Count, this, IdRemap.DiscardId, isItemSwap: false));
break;
}
}
base.IsActive = true;
InitProjSpecific();
}
partial void InitProjSpecific();
private bool linksInitialized;
public override void OnMapLoaded()
{
if (linksInitialized) { return; }
InitializeLinks();
}
public void InitializeLinks()
{
foreach (Connection c in Connections)
{
c.InitializeFromLoaded();
}
if (disconnectedWireIds != null)
{
foreach (ushort disconnectedWireId in disconnectedWireIds)
{
if (!(Entity.FindEntityByID(disconnectedWireId) is Item wireItem)) { continue; }
Wire wire = wireItem.GetComponent<Wire>();
if (wire != null)
{
if (Item.ItemList.Any(it => it != item && (it.GetComponent<ConnectionPanel>()?.DisconnectedWires.Contains(wire) ?? false)))
{
if (wire.Item.body != null) { wire.Item.body.Enabled = false; }
wire.IsActive = false;
wire.UpdateSections();
}
DisconnectedWires.Add(wire);
base.IsActive = true;
}
}
}
linksInitialized = true;
}
public override void OnItemLoaded()
{
if (item.body != null && item.body.BodyType == FarseerPhysics.BodyType.Dynamic)
{
var holdable = item.GetComponent<Holdable>();
if (holdable == null || !holdable.Attachable)
{
DebugConsole.ThrowError("Item \"" + item.Name + "\" has a ConnectionPanel component," +
" but cannot be wired because it has an active physics body that cannot be attached to a wall." +
" Remove the physics body or add a Holdable component with the Attachable attribute set to true.");
}
}
}
public void MoveConnectedWires(Vector2 amount)
{
Vector2 wireNodeOffset = item.Submarine == null ? Vector2.Zero : item.Submarine.HiddenSubPosition + amount;
foreach (Connection c in Connections)
{
foreach (Wire wire in c.Wires)
{
if (wire == null) { continue; }
TryMoveWire(wire);
}
}
foreach (var wire in DisconnectedWires)
{
TryMoveWire(wire);
}
void TryMoveWire(Wire wire)
{
#if CLIENT
if (wire.Item.IsSelected) { return; }
#endif
var wireNodes = wire.GetNodes();
if (wireNodes.Count == 0) { return; }
if (Submarine.RectContains(item.Rect, wireNodes[0] + wireNodeOffset))
{
wire.MoveNode(0, amount);
}
else if (Submarine.RectContains(item.Rect, wireNodes[wireNodes.Count - 1] + wireNodeOffset))
{
wire.MoveNode(wireNodes.Count - 1, amount);
}
}
}
public override void Update(float deltaTime, Camera cam)
{
UpdateProjSpecific(deltaTime);
if (user == null || (user.SelectedItem != item && user.SelectedSecondaryItem != item))
{
#if SERVER
if (user != null) { item.CreateServerEvent(this); }
#endif
user = null;
if (DisconnectedWires.Count == 0) { base.IsActive = false; }
return;
}
if (!user.Enabled || !HasRequiredItems(user, addMessage: false))
{
user = null;
base.IsActive = false;
return;
}
user.AnimController.UpdateUseItem(!user.IsClimbing, item.WorldPosition + new Vector2(0.0f, 100.0f) * (((float)Timing.TotalTime / 10.0f) % 0.1f));
}
public override void UpdateBroken(float deltaTime, Camera cam)
{
Update(deltaTime, cam);
}
partial void UpdateProjSpecific(float deltaTime);
public bool CanRewire()
{
if (item.Container?.GetComponent<CircuitBox>() != null)
{
return true;
}
//attaching wires to items with a body is not allowed
//(signal items remove their bodies when attached to a wall)
if (item.body != null && item.body.BodyType == FarseerPhysics.BodyType.Dynamic)
{
return false;
}
return true;
}
public override bool Select(Character picker)
{
if (!CanRewire())
{
return false;
}
user = picker;
#if SERVER
if (user != null) { item.CreateServerEvent(this); }
#endif
base.IsActive = true;
return true;
}
public override bool Use(float deltaTime, Character character = null)
{
if (character == null || character != user) { return false; }
return true;
}
/// <summary>
/// Check if the character manages to succesfully rewire the panel, and if not, apply OnFailure effects
/// </summary>
public bool CheckCharacterSuccess(Character character)
{
if (character == null) { return false; }
//no electrocution in sub editor
if (Screen.Selected == GameMain.SubEditorScreen) { return true; }
var reactor = item.GetComponent<Reactor>();
if (reactor != null)
{
//reactors that arent generating power atm can be rewired without the risk of electrical shock
if (MathUtils.NearlyEqual(reactor.CurrPowerConsumption, 0.0f)) { return true; }
}
var powerContainer = item.GetComponent<PowerContainer>();
if (powerContainer != null)
{
//empty batteries/supercapacitors can be rewired without the risk of electrical shock
//non-empty ones always have a chance of zapping the user
if (powerContainer.Charge <= 0.0f) { return true; }
}
var powered = item.GetComponent<Powered>();
if (powered != null && powerContainer == null)
{
//unpowered panels can be rewired without the risk of electrical shock
if (powered.Voltage < 0.1f) { return true; }
}
float degreeOfSuccess = DegreeOfSuccess(character);
if (Rand.Range(0.0f, 0.5f) < degreeOfSuccess) { return true; }
ApplyStatusEffects(ActionType.OnFailure, 1.0f, character);
return false;
}
public override void Load(ContentXElement element, bool usePrefabValues, IdRemap idRemap, bool isItemSwap)
{
base.Load(element, usePrefabValues, idRemap, isItemSwap);
List<Connection> loadedConnections = new List<Connection>();
foreach (var subElement in element.Elements())
{
switch (subElement.Name.ToString())
{
case "input":
loadedConnections.Add(new Connection(subElement, connectionIndex: loadedConnections.Count, this, idRemap, isItemSwap));
break;
case "output":
loadedConnections.Add(new Connection(subElement, connectionIndex: loadedConnections.Count, this, idRemap, isItemSwap));
break;
}
}
for (int i = 0; i < loadedConnections.Count && i < Connections.Count; i++)
{
Connections[i].LoadedWires.Clear();
Connections[i].LoadedWires.AddRange(loadedConnections[i].LoadedWires);
}
disconnectedWireIds = element.GetAttributeUshortArray("disconnectedwires", Array.Empty<ushort>()).ToList();
for (int i = 0; i < disconnectedWireIds.Count; i++)
{
disconnectedWireIds[i] = idRemap.GetOffsetId(disconnectedWireIds[i]);
}
}
public override XElement Save(XElement parentElement)
{
XElement componentElement = base.Save(parentElement);
foreach (Connection c in Connections)
{
c.Save(componentElement);
}
if (DisconnectedWires.Count > 0)
{
componentElement.Add(new XAttribute("disconnectedwires", string.Join(",", DisconnectedWires.Select(w => w.Item.ID))));
}
return componentElement;
}
protected override void ShallowRemoveComponentSpecific()
{
//do nothing
}
protected override void RemoveComponentSpecific()
{
base.RemoveComponentSpecific();
foreach (Wire wire in DisconnectedWires.ToList())
{
if (wire.OtherConnection(null) == null) //wire not connected to anything else
{
#if CLIENT
if (SubEditorScreen.IsSubEditor())
{
wire.Item.Remove();
}
else
{
wire.Item.Drop(null);
}
#else
wire.Item.Drop(null);
#endif
}
}
DisconnectedWires.Clear();
foreach (Connection c in Connections)
{
foreach (Wire wire in c.Wires.ToArray())
{
if (wire.OtherConnection(c) == null) //wire not connected to anything else
{
#if CLIENT
if (SubEditorScreen.IsSubEditor())
{
wire.Item.Remove();
}
else
{
wire.Item.Drop(null);
}
#else
wire.Item.Drop(null);
#endif
}
else
{
wire.RemoveConnection(item);
}
}
c.Grid = null;
}
foreach (var connection in Connections)
{
Powered.ChangedConnections.Remove(connection);
connection.Recipients.Clear();
}
Connections.Clear();
#if CLIENT
rewireSoundChannel?.FadeOutAndDispose();
rewireSoundChannel = null;
#endif
}
public override void ReceiveSignal(Signal signal, Connection connection)
{
//do nothing
}
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
{
#if CLIENT
TriggerRewiringSound();
#endif
msg.WriteByte((byte)Connections.Count);
foreach (Connection connection in Connections)
{
msg.WriteVariableUInt32((uint)connection.Wires.Count);
foreach (Wire wire in connection.Wires)
{
msg.WriteUInt16(wire?.Item == null ? (ushort)0 : wire.Item.ID);
}
}
msg.WriteUInt16((ushort)DisconnectedWires.Count);
foreach (Wire disconnectedWire in DisconnectedWires)
{
msg.WriteUInt16(disconnectedWire.Item.ID);
}
}
}
}