Client LuaCsForBarotrauma
RelayComponent.cs
2 using Microsoft.Xna.Framework;
3 using System;
4 using System.Collections.Generic;
5 using System.Linq;
6 using System.Xml.Linq;
7 
9 {
11  {
12  private float maxPower;
13 
14  private bool isOn;
15 
16  private float prevVoltage;
17 
18  private float? newVoltage = null;
19 
20  //internal load buffer that's used to isolate the input and output sides from each other
21  private float internalLoadBuffer = 0;
22 
23  //previous load (used to smooth changes in the load to prevent oscillations)
24  private float prevInternalLoad = 0;
25 
26  //previous load on the output side
27  private float prevExternalLoad = 0;
28 
29  //difference between the internal load buffer and external load
30  private float bufferDiff = 0;
31 
32  private float thirdInverseMax = 0, loadEqnConstant = 0;
33 
34  private static readonly Dictionary<string, string> connectionPairs = new Dictionary<string, string>
35  {
36  { "power_in", "power_out"},
37  { "signal_in", "signal_out" },
38  { "signal_in1", "signal_out1" },
39  { "signal_in2", "signal_out2" },
40  { "signal_in3", "signal_out3" },
41  { "signal_in4", "signal_out4" },
42  { "signal_in5", "signal_out5" }
43  };
44 
45  protected override PowerPriority Priority { get { return PowerPriority.Relay; } }
46 
47  public float DisplayLoad
48  {
49  get
50  {
51  if (powerOut != null && powerOut.Grid != null)
52  {
53  return powerOut.Grid.Load;
54  }
55  else
56  {
57  return 0;
58  }
59  }
60  }
61 
62  [Editable, Serialize(1000.0f, IsPropertySaveable.Yes, description: "The maximum amount of power that can pass through the item.")]
63  public float MaxPower
64  {
65  get { return maxPower; }
66  set
67  {
68  maxPower = Math.Max(0.0f, value);
69  SetLoadFormulaValues();
70  }
71  }
72 
73  [InGameEditable, Serialize(true, IsPropertySaveable.Yes, description: "Can the relay currently pass power and signals through it.", alwaysUseInstanceValues: true)]
74  public bool IsOn
75  {
76  get
77  {
78  return isOn;
79  }
80  set
81  {
82  isOn = value;
83  CanTransfer = value;
84  if (!isOn)
85  {
86  currPowerConsumption = 0.0f;
87  }
88  }
89  }
90 
92  : base(item, element)
93  {
94  IsActive = true;
95  prevVoltage = 0;
96  SetLoadFormulaValues();
97  }
98 
99  private void SetLoadFormulaValues()
100  {
101  internalLoadBuffer = MaxPower * 2;
102  // Set constants for load Formula to reduce calculation time
103  thirdInverseMax = 1 / (3 * maxPower);
104  loadEqnConstant = (8 * maxPower) / 3;
105  }
106 
107 
108  public override void OnItemLoaded()
109  {
110  base.OnItemLoaded();
111  var connections = Item.Connections;
112  if (connections != null)
113  {
114  foreach (KeyValuePair<string, string> connectionPair in connectionPairs)
115  {
116  if (connections.Any(c => c.Name == connectionPair.Key) && !connections.Any(c => c.Name == connectionPair.Value))
117  {
118  DebugConsole.ThrowError("Error in item \"" + Name + "\" - matching connection pair not found for the connection \"" + connectionPair.Key + "\" (expecting \"" + connectionPair.Value + "\").");
119  }
120  else if (connections.Any(c => c.Name == connectionPair.Value) && !connections.Any(c => c.Name == connectionPair.Key))
121  {
122  DebugConsole.ThrowError("Error in item \"" + Name + "\" - matching connection pair not found for the connection \"" + connectionPair.Value + "\" (expecting \"" + connectionPair.Key + "\").");
123  }
124  }
125  }
126  }
127 
128  public override void Update(float deltaTime, Camera cam)
129  {
131 
132  item.SendSignal(IsOn ? "1" : "0", "state_out");
133  item.SendSignal(((int)Math.Round(-PowerLoad)).ToString(), "power_value_out");
134  item.SendSignal(((int)Math.Round(DisplayLoad)).ToString(), "load_value_out");
135 
136  if (isBroken)
137  {
139  isBroken = false;
140  }
141 
142  ApplyStatusEffects(ActionType.OnActive, deltaTime);
143 
145  {
146  item.Condition = 0.0f;
147  }
148  }
149 
154  public override float GetCurrentPowerConsumption(Connection connection = null)
155  {
156  //Can't output or draw if broken
157  if (isBroken)
158  {
159  return 0;
160  }
161 
162  if (connection == powerIn)
163  {
164  float currentLoad = MaxPower;
165  if (internalLoadBuffer > MaxPower)
166  {
167  //Buffer load charging curve - special relay sauce
168  // Original formula (buffer - 3 * maxPower)^2 / (3 * maxPower) - (maxPower / 3)
169  //loadDraw = MathHelper.Clamp((float)Math.Pow(internalBuffer - 3 * MaxPower, 2) / (3 * MaxPower) - MaxPower / 3, 1, MaxPower);
170  //Optimised formula 0.2% error from original
171  currentLoad = MathHelper.Clamp(internalLoadBuffer * internalLoadBuffer * thirdInverseMax - 2 * internalLoadBuffer + loadEqnConstant, 0.001f, MaxPower);
172 
173  //Slight smoothing to load to minimise relay jank
174  currentLoad = MathHelper.Clamp((currentLoad + prevInternalLoad * 0.1f) / 1.1f, 0.001f, MaxPower);
175  prevInternalLoad = currentLoad;
176  }
177 
178  //Add on extra load after calculation
179  currentLoad += ExtraLoad;
180  return currentLoad;
181  }
182  else
183  {
184  //Flag output as power out
185  return -1;
186  }
187  }
188 
189  private bool RelayCanOutput()
190  {
191  //Only allow output if device is on, buffers have charge and the connected grids aren't short circuited
192  return isOn && powerIn != null && powerIn.Grid != null && internalLoadBuffer > 0 && powerOut != null && powerOut.Grid != null && powerIn.Grid != powerOut.Grid;
193  }
194 
199  public override PowerRange MinMaxPowerOut(Connection connection, float load = 0)
200  {
201  if (connection == powerOut)
202  {
203  if (RelayCanOutput())
204  {
205  //Determine output limits from buffer and voltage
206  float bufferDraw = MathHelper.Min(internalLoadBuffer, MaxPower);
207  float voltageLimit = MathHelper.Min(MaxPower, load) * prevVoltage;
208 
209  //If undervolted adjust max output so that other relays can compensate
210  if (prevVoltage < 1)
211  {
212  voltageLimit *= prevVoltage;
213  }
214 
215  float maxOutput = MathHelper.Min(voltageLimit, bufferDraw);
216  return new PowerRange(0.0f, maxOutput);
217  }
218  }
219 
220  return PowerRange.Zero;
221  }
222 
229  public override float GetConnectionPowerOut(Connection connection, float power, PowerRange minMaxPower, float load)
230  {
231  if (connection == powerIn)
232  {
233  return 0.0f;
234  }
235  else if (RelayCanOutput())
236  {
237  //Determine output limits of the relay
238  float bufferDraw = MathHelper.Min(internalLoadBuffer, MaxPower);
239  float voltageLimit = MathHelper.Min(MaxPower, load) * prevVoltage;
240  float maxOut = MathHelper.Min(voltageLimit, bufferDraw);
241 
242  //Don't output negative power to the grid
243  if (maxOut < 0)
244  {
245  PowerLoad = 0;
246  return 0;
247  }
248 
249  prevExternalLoad = load;
250 
251  //Calculate power out
252  PowerLoad = MathHelper.Clamp((load * prevVoltage - power) / MathHelper.Max(minMaxPower.Max, 1E-20f) * -maxOut, -maxOut, 0);
253  return -PowerLoad;
254  }
255 
256  //Else relay isn't outputting
257  PowerLoad = 0;
258  return 0;
259  }
260 
265  public override void GridResolved(Connection conn)
266  {
267  if (conn == powerIn)
268  {
269  if (powerIn != null && powerIn.Grid != null)
270  {
271  float addToBuffer = powerIn.Grid.Voltage * (CurrPowerConsumption - ExtraLoad);
272 
273  //Limit power input to the previous voltage to prevent wild oscillations in overload
274  if (powerIn.Grid.Voltage > 1)
275  {
276  addToBuffer = prevVoltage * (CurrPowerConsumption - ExtraLoad);
277  }
278 
279  //Cap the max power input
280  if (addToBuffer > MaxPower)
281  {
282  addToBuffer = MaxPower;
283  }
284 
285  //To prevent problems with grid order, only update voltage and buffer after input and output grids have been resolved
286  if (newVoltage == null)
287  {
288  //temporarily store the new voltage and also indicates that the input connection side has been updated
289  newVoltage = powerIn.Grid.Voltage;
290  bufferDiff = addToBuffer;
291  }
292  else
293  {
294  UpdateBuffer(addToBuffer, powerIn.Grid.Voltage);
295  }
296  }
297  }
298  else
299  {
300  //To prevent problems with grid order, only update voltage and buffer after input and output grids have been resolved
301  if (newVoltage == null)
302  {
303  //Flag that output connection has been updated already
304  newVoltage = -1;
305  bufferDiff = PowerLoad;
306  }
307  else
308  {
309  UpdateBuffer(PowerLoad, (float)newVoltage);
310  }
311  }
312  }
313 
314  private void UpdateBuffer(float addToBuffer, float newVoltage)
315  {
316  //Update buffer and voltage
317  float limit = MaxPower * 2;
318 
319  //Clamp the buffer to have a constant load in a severe overload event, otherwise wild oscillation will occur
320  if (RelayCanOutput() && powerIn.Grid.Voltage > 2)
321  {
322  limit = MathHelper.Min(limit, 3 * MaxPower - (float)Math.Sqrt((3 * prevExternalLoad + MaxPower) * MaxPower));
323  }
324 
325  //Add to the internal buffer
326  internalLoadBuffer = MathHelper.Clamp(internalLoadBuffer + bufferDiff + addToBuffer, 0, limit);
327 
328  //Decay overvoltage slightly, helps resolve large chain loops to a grid
329  if (newVoltage > 1)
330  {
331  newVoltage = MathHelper.Max(newVoltage - 0.0005f, 1);
332  }
333 
334  prevVoltage = newVoltage;
335  this.newVoltage = null;
336  bufferDiff = 0;
337  }
338 
339  public override void ReceiveSignal(Signal signal, Connection connection)
340  {
341  if (item.Condition <= 0.0f || connection.IsPower) { return; }
342 
343  if (connectionPairs.TryGetValue(connection.Name, out string outConnection))
344  {
345  if (!IsOn) { return; }
346  item.SendSignal(signal, outConnection);
347  }
348  else if (connection.Name == "toggle")
349  {
350  if (signal.value == "0") { return; }
351  SetState(!IsOn, false);
352  }
353  else if (connection.Name == "set_state")
354  {
355  SetState(signal.value != "0", false);
356  }
357  }
358 
359  public void SetState(bool on, bool isNetworkMessage)
360  {
361 #if CLIENT
362  if (GameMain.Client != null && !isNetworkMessage) { return; }
363 #endif
364 
365 #if SERVER
366  if (on != IsOn && GameMain.Server != null)
367  {
368  item.CreateServerEvent(this);
369  }
370 #endif
371 
372  IsOn = on;
373  }
374 
375  public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
376  {
377  msg.WriteBoolean(isOn);
378  }
379 
380  public void ClientEventRead(IReadMessage msg, float sendingTime)
381  {
382  SetState(msg.ReadBoolean(), true);
383  }
384  }
385 }
static GameClient Client
Definition: GameMain.cs:188
void SendSignal(string signal, string connectionName)
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)
float ExtraLoad
Additional load coming from somewhere else than the devices connected to the junction box (e....
float currPowerConsumption
The amount of power currently consumed by the item. Negative values mean that the item is providing p...
override void GridResolved(Connection conn)
Connection's grid resolved, determine the difference to be added to the buffer. Ensure the prevVoltag...
override float GetConnectionPowerOut(Connection connection, float power, PowerRange minMaxPower, float load)
Power out for the relay connection. Relay will output the necessary power to the grid based on maximu...
void ClientEventRead(IReadMessage msg, float sendingTime)
override float GetCurrentPowerConsumption(Connection connection=null)
Relay power consumption. Load consumption is based on the internal buffer. This allows for the relay ...
void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData=null)
void SetState(bool on, bool isNetworkMessage)
RelayComponent(Item item, ContentXElement element)
override void OnItemLoaded()
Called when all the components of the item have been loaded. Use to initialize connections between co...
override PowerRange MinMaxPowerOut(Connection connection, float load=0)
Minimum and maximum power out for the relay. Max out is adjusted to allow for other relays to compens...
override void ReceiveSignal(Signal signal, Connection connection)
override void Update(float deltaTime, Camera cam)
Interface for entities that the server can send events to the clients
PowerPriority
Order in which power sources will provide to a grid, lower number is higher priority
ActionType
ActionTypes define when a StatusEffect is executed.
Definition: Enums.cs:19