Client LuaCsForBarotrauma
BarotraumaShared/SharedSource/Items/Components/ElectricalDischarger.cs
3 using FarseerPhysics;
4 using Microsoft.Xna.Framework;
5 using System;
6 using System.Collections.Generic;
7 using System.Linq;
8 
10 {
12  {
13  private static readonly List<ElectricalDischarger> list = new List<ElectricalDischarger>();
14  public static IEnumerable<ElectricalDischarger> List
15  {
16  get { return list; }
17  }
18 
19  const int MaxNodes = 100;
20  const float MaxNodeDistance = 150.0f;
21 
22  public struct Node
23  {
24  public Vector2 WorldPosition;
25  public int ParentIndex;
26  public float Length;
27  public float Angle;
28 
29  public Node(Vector2 worldPosition, int parentIndex, float length = 0.0f, float angle = 0.0f)
30  {
31  WorldPosition = worldPosition;
32  ParentIndex = parentIndex;
33  Length = length;
34  Angle = angle;
35  }
36  }
37 
38  public override bool IsActive
39  {
40  get { return base.IsActive; }
41  set
42  {
43  base.IsActive = value;
44  if (!value)
45  {
46  nodes.Clear();
47  charactersInRange.Clear();
48  }
49  }
50  }
51 
52  [Serialize(500.0f, IsPropertySaveable.Yes, description: "How far the discharge can travel from the item.", alwaysUseInstanceValues: true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 5000.0f)]
53  public float Range
54  {
55  get;
56  set;
57  }
58 
59  [Serialize(25.0f, IsPropertySaveable.Yes, description: "How much further can the discharge be carried when moving across walls.", alwaysUseInstanceValues: true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f)]
61  {
62  get;
63  set;
64  }
65 
66  [Serialize(0.0f, IsPropertySaveable.No)]
67  public float RaycastRange { get; set; }
68 
69  [Serialize(0.25f, IsPropertySaveable.Yes, description: "The duration of an individual discharge (in seconds)."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 60.0f, ValueStep = 0.1f, DecimalCount = 2)]
70  public float Duration
71  {
72  get;
73  set;
74  }
75 
76  [Serialize(0.25f, IsPropertySaveable.Yes), Editable(MinValueFloat = 0.0f, MaxValueFloat = 60.0f, ValueStep = 0.1f, DecimalCount = 2)]
77  public float Reload
78  {
79  get;
80  set;
81  }
82 
83  [Serialize(false, IsPropertySaveable.Yes, "If set to true, the discharge cannot travel inside the submarine nor shock anyone inside."), Editable]
84  public bool OutdoorsOnly
85  {
86  get;
87  set;
88  }
89 
90  [Serialize(false, IsPropertySaveable.Yes)]
91  public bool IgnoreUser
92  {
93  get;
94  set;
95  }
96 
97  private readonly List<Node> nodes = new List<Node>();
98  public IEnumerable<Node> Nodes
99  {
100  get { return nodes; }
101  }
102 
103  private readonly List<(Character character, Node node)> charactersInRange = new List<(Character character, Node node)>();
104 
105  private bool charging;
106 
107  private float timer;
108 
109  private readonly Attack attack;
110 
111  private Character user;
112 
113  private float reloadTimer;
114 
116  base(item, element)
117  {
118  list.Add(this);
119 
120  foreach (var subElement in element.Elements())
121  {
122  switch (subElement.Name.ToString().ToLowerInvariant())
123  {
124  case "attack":
125  attack = new Attack(subElement, item.Name);
126  break;
127  }
128  }
129 
130  InitProjSpecific();
131  }
132 
133  partial void InitProjSpecific();
134 
135  public override bool Use(float deltaTime, Character character = null)
136  {
137  //already active, do nothing
138  if (IsActive) { return false; }
139  if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return false; }
140  if (character != null && !CharacterUsable) { return false; }
141 
142  charging = true;
143  timer = Duration;
144  IsActive = true;
145  user = character;
146 #if SERVER
147  if (GameMain.Server != null) { item.CreateServerEvent(this); }
148 #endif
149  return false;
150  }
151 
152  public override void Update(float deltaTime, Camera cam)
153  {
154 #if CLIENT
155  frameOffset = Rand.Int(electricitySprite.FrameCount);
156 #endif
157  if (timer <= 0.0f)
158  {
159  if (reloadTimer > 0.0f)
160  {
161  reloadTimer -= deltaTime;
162  return;
163  }
164  IsActive = false;
165  return;
166  }
167 
168  timer -= deltaTime;
169  if (charging)
170  {
171  bool hasPower = false;
172  if (item.Connections == null)
173  {
174  //no connections and can't be wired = must be powered by something like batteries
175  hasPower = Voltage > MinVoltage;
176  }
177  else
178  {
180  }
181 
182  if (hasPower)
183  {
184  var batteries = GetDirectlyConnectedBatteries().Where(static b => !b.OutputDisabled && b.Charge > 0.0001f && b.MaxOutPut > 0.0001f);
185  float neededPower = PowerConsumption;
186  while (neededPower > 0.0001f && batteries.Any())
187  {
188  float takePower = neededPower / batteries.Count();
189  takePower = Math.Min(takePower, batteries.Min(b => Math.Min(b.Charge * 3600.0f, b.MaxOutPut)));
190  foreach (PowerContainer battery in batteries)
191  {
192  neededPower -= takePower;
193  battery.Charge -= takePower / 3600.0f;
194 #if SERVER
195  if (GameMain.Server != null) { battery.Item.CreateServerEvent(battery); }
196 #endif
197  }
198  }
199  Discharge();
200  }
201  }
202  }
203 
207  public override float GetCurrentPowerConsumption(Connection conn = null)
208  {
209  return 0;
210  }
211 
212  public override void UpdateBroken(float deltaTime, Camera cam)
213  {
214  base.UpdateBroken(deltaTime, cam);
215  nodes.Clear();
216  charactersInRange.Clear();
217  }
218 
219  private void Discharge()
220  {
221  reloadTimer = Reload;
222  ApplyStatusEffects(ActionType.OnUse, 1.0f);
224  if (attack != null)
225  {
226  foreach ((Character character, Node node) in charactersInRange)
227  {
228  if (character == null || character.Removed) { continue; }
229  character.ApplyAttack(user, node.WorldPosition, attack, MathHelper.Clamp(Voltage, 1.0f, MaxOverVoltageFactor),
230  impulseDirection: character.WorldPosition - node.WorldPosition);
231  }
232  }
233  DischargeProjSpecific();
234  charging = false;
235  }
236 
237  partial void DischargeProjSpecific();
238 
239  public void FindNodes(Vector2 worldPosition, float range)
240  {
241  if (RaycastRange > 0.0f)
242  {
243  float angle = 0.0f;
244  float dir = 1;
245  if (item.body != null)
246  {
247  angle += item.body.Rotation;
248  dir = item.body.Dir;
249  }
250  worldPosition += new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle)) * RaycastRange * dir;
251  }
252 
253  //see which submarines are within range so we can skip structures that are in far-away subs
254  List<Submarine> submarinesInRange = new List<Submarine>();
255  foreach (Submarine sub in Submarine.Loaded)
256  {
257  if (item.Submarine == sub)
258  {
259  submarinesInRange.Add(sub);
260  }
261  else if (sub != null)
262  {
263  Rectangle subBorders = new Rectangle(
264  sub.Borders.X - (int)range, sub.Borders.Y + (int)range,
265  sub.Borders.Width + (int)(range * 2), sub.Borders.Height + (int)(range * 2));
266  subBorders.Location += MathUtils.ToPoint(sub.SubBody.Position);
267  if (Submarine.RectContains(subBorders, worldPosition))
268  {
269  submarinesInRange.Add(sub);
270  }
271  }
272  }
273 
274  //get all walls within range the arc could potentially hit
275  List<Entity> entitiesInRange = new List<Entity>(100);
276  foreach (Structure structure in Structure.WallList)
277  {
278  if (!structure.HasBody || structure.IsPlatform) { continue; }
279  if (structure.Submarine != null&& !submarinesInRange.Contains(structure.Submarine)) { continue; }
280 
281  var structureWorldRect = structure.WorldRect;
282  if (worldPosition.X < structureWorldRect.X - range) { continue; }
283  if (worldPosition.X > structureWorldRect.Right + range) { continue; }
284  if (worldPosition.Y > structureWorldRect.Y + range) { continue; }
285  if (worldPosition.Y < structureWorldRect.Y - structureWorldRect.Height - range) { continue; }
286 
287  if (structure.Submarine != null)
288  {
289  if (!submarinesInRange.Contains(structure.Submarine)) { continue; }
290  if (OutdoorsOnly)
291  {
292  //check if there's a hull at either side of the wall
293  Vector2 normal = new Vector2(
294  (float)-Math.Sin(structure.IsHorizontal ? -structure.BodyRotation : MathHelper.PiOver2 - structure.BodyRotation),
295  (float)Math.Cos(structure.IsHorizontal ? -structure.BodyRotation : MathHelper.PiOver2 - structure.BodyRotation));
296  Vector2 structurePos = structure.Position;
297  float offsetAmount = Submarine.GridSize.X * 2;
298  if (structure.HasBody)
299  {
300  structurePos = ConvertUnits.ToDisplayUnits(structure.Bodies.First().Position);
301  offsetAmount = Math.Max(
302  offsetAmount,
303  structure.IsHorizontal ? structure.BodyHeight : structure.BodyWidth);
304  }
305  if (Hull.FindHull(structurePos + normal * offsetAmount, useWorldCoordinates: false) != null &&
306  Hull.FindHull(structurePos - normal * offsetAmount, useWorldCoordinates: false) != null)
307  {
308  continue;
309  }
310  }
311  }
312 
313  entitiesInRange.Add(structure);
314  }
315 
316 
317  nodes.Clear();
318  if (RaycastRange > 0.0f)
319  {
320  nodes.Add(new Node(item.WorldPosition, -1));
321  int parentNodeIndex = 0;
322  AddNodesBetweenPoints(item.WorldPosition, worldPosition, 0.5f, ref parentNodeIndex);
323  }
324  else
325  {
326  nodes.Add(new Node(worldPosition, -1));
327  }
328 
329  //get all characters within range the arc could potentially hit
330  float totalRange = RaycastRange + range;
331  foreach (Character character in Character.CharacterList)
332  {
333  if (!character.Enabled) { continue; }
334  if (IgnoreUser && character == user) { continue; }
335  if (OutdoorsOnly && character.Submarine != null) { continue; }
336  if (character.Submarine != null && !submarinesInRange.Contains(character.Submarine)) { continue; }
337 
338  if (Vector2.DistanceSquared(character.WorldPosition, worldPosition) < totalRange * totalRange * RangeMultiplierInWalls)
339  {
340  entitiesInRange.Add(character);
341  }
342  //if the weapon does a raycast, check distance to the ray too (not just the end of the ray)
343  if (RaycastRange > 0)
344  {
345  float distSqr = MathUtils.LineSegmentToPointDistanceSquared(worldPosition, item.WorldPosition, character.WorldPosition);
346  //if the distance from the initial raycast to the character is small (e.g. goes through the character), we know it must hit
347  if (distSqr < range * range * RangeMultiplierInWalls)
348  {
349  if (!entitiesInRange.Contains(character)) { entitiesInRange.Add(character); }
350  charactersInRange.Add((character, nodes.First()));
351  }
352  }
353  }
354 
355  FindNodes(entitiesInRange, worldPosition, nodes.Count - 1, range);
356 
357  //construct final nodes (w/ lengths and angles so they don't have to be recalculated when rendering the discharge)
358  for (int i = 0; i < nodes.Count; i++)
359  {
360  if (nodes[i].ParentIndex < 0) { continue; }
361  Node parentNode = nodes[nodes[i].ParentIndex];
362  float length = Vector2.Distance(nodes[i].WorldPosition, parentNode.WorldPosition) * Rand.Range(1.0f, 1.25f);
363  float angle = MathUtils.VectorToAngle(parentNode.WorldPosition - nodes[i].WorldPosition);
364  nodes[i] = new Node(nodes[i].WorldPosition, nodes[i].ParentIndex, length, angle);
365  }
366  }
367 
368  private void FindNodes(List<Entity> entitiesInRange, Vector2 currPos, int parentNodeIndex, float currentRange)
369  {
370  if (currentRange <= 0.0f || nodes.Count >= MaxNodes) { return; }
371 
372  //find the closest structure
373  int closestIndex = -1;
374  float closestDist = float.MaxValue;
375  for (int i = 0; i < entitiesInRange.Count; i++)
376  {
377  float dist = float.MaxValue;
378  if (entitiesInRange[i] is Structure structure)
379  {
380  if (structure.IsHorizontal)
381  {
382  dist = Math.Abs(structure.WorldPosition.Y - currPos.Y);
383  if (currPos.X < structure.WorldRect.X)
384  dist += structure.WorldRect.X - currPos.X;
385  else if (currPos.X > structure.WorldRect.Right)
386  dist += currPos.X - structure.WorldRect.Right;
387  }
388  else
389  {
390  dist = Math.Abs(structure.WorldPosition.X - currPos.X);
391  if (currPos.Y < structure.WorldRect.Y - structure.Rect.Height)
392  dist += (structure.WorldRect.Y - structure.Rect.Height) - currPos.Y;
393  else if (currPos.Y > structure.WorldRect.Y)
394  dist += currPos.Y - structure.WorldRect.Y;
395  }
396  }
397  else if (entitiesInRange[i] is Character character)
398  {
399  dist = MathF.Sqrt(MathUtils.LineSegmentToPointDistanceSquared(currPos, nodes[parentNodeIndex].WorldPosition, character.WorldPosition));
400  }
401 
402  if (dist < closestDist)
403  {
404  closestIndex = i;
405  closestDist = dist;
406  }
407  }
408 
409  if (closestIndex == -1 || closestDist > currentRange)
410  {
411  //nothing in range, create some arcs to random directions
412  for (int i = 0; i < Rand.Int(4); i++)
413  {
414  Vector2 targetPos = currPos + Rand.Vector(MaxNodeDistance * Rand.Range(0.5f, 1.5f));
415  nodes.Add(new Node(targetPos, parentNodeIndex));
416  }
417  return;
418  }
419  currentRange -= closestDist;
420 
421  if (entitiesInRange[closestIndex] is Structure targetStructure)
422  {
423  if (targetStructure.IsHorizontal)
424  {
425  //which side of the structure to add the nodes to
426  //if outside the sub, use the sides that's furthers from the sub's center position
427  //otherwise the side that's closer to the previous node
428  int yDir = OutdoorsOnly && targetStructure.Submarine != null ?
429  Math.Sign(targetStructure.WorldPosition.Y - targetStructure.Submarine.WorldPosition.Y) :
430  Math.Sign(currPos.Y - targetStructure.WorldPosition.Y);
431 
432  int sectionIndex = targetStructure.FindSectionIndex(currPos, world: true, clamp: true);
433  if (sectionIndex == -1) { return; }
434  Vector2 sectionPos = targetStructure.SectionPosition(sectionIndex, world: true);
435  Vector2 targetPos =
436  new Vector2(
437  MathHelper.Clamp(sectionPos.X, targetStructure.WorldRect.X, targetStructure.WorldRect.Right),
438  sectionPos.Y + targetStructure.BodyHeight / 2 * yDir);
439 
440  //create nodes from the current position to the closest point on the structure
441  AddNodesBetweenPoints(currPos, targetPos, 0.25f, ref parentNodeIndex);
442 
443  //add a node at the closest point
444  nodes.Add(new Node(targetPos, parentNodeIndex));
445  int nodeIndex = nodes.Count - 1;
446  entitiesInRange.RemoveAt(closestIndex);
447 
448  float newRange = currentRange - (targetStructure.Rect.Width / 2) * (1.0f / RangeMultiplierInWalls);
449 
450  //continue the discharge to the left edge of the structure and extend from there
451  int leftNodeIndex = nodeIndex;
452  Vector2 leftPos = targetStructure.SectionPosition(0, world: true);
453  leftPos.Y += targetStructure.BodyHeight / 2 * yDir;
454  AddNodesBetweenPoints(targetPos, leftPos, 0.05f, ref leftNodeIndex);
455  nodes.Add(new Node(leftPos, leftNodeIndex));
456  FindNodes(entitiesInRange, leftPos, nodes.Count - 1, newRange);
457 
458  //continue the discharge to the right edge of the structure and extend from there
459  int rightNodeIndex = nodeIndex;
460  Vector2 rightPos = targetStructure.SectionPosition(targetStructure.SectionCount - 1, world: true);
461  leftPos.Y += targetStructure.BodyHeight / 2 * yDir;
462  AddNodesBetweenPoints(targetPos, rightPos, 0.05f, ref rightNodeIndex);
463  nodes.Add(new Node(rightPos, rightNodeIndex));
464  FindNodes(entitiesInRange, rightPos, nodes.Count - 1, newRange);
465  }
466  else
467  {
468  int xDir = OutdoorsOnly && targetStructure.Submarine != null ?
469  Math.Sign(targetStructure.WorldPosition.X - targetStructure.Submarine.WorldPosition.X) :
470  Math.Sign(currPos.X - targetStructure.WorldPosition.X);
471 
472  int sectionIndex = targetStructure.FindSectionIndex(currPos, world: true, clamp: true);
473  if (sectionIndex == -1) { return; }
474  Vector2 sectionPos = targetStructure.SectionPosition(sectionIndex, world: true);
475 
476  Vector2 targetPos = new Vector2(
477  sectionPos.X + targetStructure.BodyWidth / 2 * xDir,
478  MathHelper.Clamp(sectionPos.Y, targetStructure.WorldRect.Y - targetStructure.Rect.Height, targetStructure.WorldRect.Y));
479 
480  //create nodes from the current position to the closest point on the structure
481  AddNodesBetweenPoints(currPos, targetPos, 0.25f, ref parentNodeIndex);
482 
483  //add a node at the closest point
484  nodes.Add(new Node(targetPos, parentNodeIndex));
485  int nodeIndex = nodes.Count - 1;
486  entitiesInRange.RemoveAt(closestIndex);
487 
488  float newRange = currentRange - (targetStructure.Rect.Height / 2) * (1.0f / RangeMultiplierInWalls);
489 
490  //continue the discharge to the top edge of the structure and extend from there
491  int topNodeIndex = nodeIndex;
492  Vector2 topPos = targetStructure.SectionPosition(0, world: true);
493  topPos.X += targetStructure.BodyWidth / 2 * xDir;
494  AddNodesBetweenPoints(targetPos, topPos, 0.05f, ref topNodeIndex);
495  nodes.Add(new Node(topPos, topNodeIndex));
496  FindNodes(entitiesInRange, topPos, nodes.Count - 1, newRange);
497 
498  //continue the discharge to the bottom edge of the structure and extend from there
499  int bottomNodeIndex = nodeIndex;
500  Vector2 bottomBos = targetStructure.SectionPosition(targetStructure.SectionCount - 1, world: true);
501  bottomBos.X += targetStructure.BodyWidth / 2 * xDir;
502  AddNodesBetweenPoints(targetPos, bottomBos, 0.05f, ref bottomNodeIndex);
503  nodes.Add(new Node(bottomBos, bottomNodeIndex));
504  FindNodes(entitiesInRange, bottomBos, nodes.Count - 1, newRange);
505  }
506 
507  //check if any character is close to this structure
508  for (int j = 0; j < entitiesInRange.Count; j++)
509  {
510  var otherEntity = entitiesInRange[j];
511  if (otherEntity is not Character character) { continue; }
512  if (IgnoreUser && character == user) { continue; }
513  if (OutdoorsOnly && character.Submarine != null) { continue; }
514 
515  Vector2 characterMin = new Vector2(character.AnimController.Limbs.Min(l => l.WorldPosition.X), character.AnimController.Limbs.Min(l => l.WorldPosition.Y));
516  Vector2 characterMax = new Vector2(character.AnimController.Limbs.Max(l => l.WorldPosition.X), character.AnimController.Limbs.Max(l => l.WorldPosition.Y));
517  if (targetStructure.IsHorizontal)
518  {
519  if (characterMax.X < targetStructure.WorldRect.X) { continue; }
520  if (characterMin.X > targetStructure.WorldRect.Right) { continue; }
521  if (Math.Abs(characterMin.Y - targetStructure.WorldPosition.Y) > currentRange &&
522  Math.Abs(characterMax.Y - targetStructure.WorldPosition.Y) > currentRange)
523  {
524  continue;
525  }
526  }
527  else
528  {
529  if (characterMax.Y < targetStructure.WorldRect.Y - targetStructure.Rect.Height) { continue; }
530  if (characterMin.Y > targetStructure.WorldRect.Y) { continue; }
531  if (Math.Abs(characterMin.X - targetStructure.WorldPosition.X) > currentRange &&
532  Math.Abs(characterMax.X - targetStructure.WorldPosition.X) > currentRange)
533  {
534  continue;
535  }
536  }
537  if (!charactersInRange.Any(c => c.character == character))
538  {
539  charactersInRange.Add((character, nodes[parentNodeIndex]));
540  }
541  float closestNodeDistSqr = float.MaxValue;
542  int closestNodeIndex = -1;
543  for (int i = 0; i < nodes.Count; i++)
544  {
545  float distSqr = Vector2.DistanceSquared(character.WorldPosition, nodes[i].WorldPosition);
546  if (distSqr < closestNodeDistSqr)
547  {
548  closestNodeDistSqr = distSqr;
549  closestNodeIndex = i;
550  }
551  }
552  if (closestNodeIndex > -1)
553  {
554  FindNodes(entitiesInRange, nodes[closestNodeIndex].WorldPosition, closestNodeIndex, currentRange - (float)Math.Sqrt(closestNodeDistSqr));
555  }
556  }
557  }
558  else if (entitiesInRange[closestIndex] is Character character)
559  {
560  Vector2 targetPos = character.WorldPosition;
561  //create nodes from the current position to the closest point on the character
562  AddNodesBetweenPoints(currPos, targetPos, 0.25f, ref parentNodeIndex);
563  nodes.Add(new Node(targetPos, parentNodeIndex));
564  entitiesInRange.RemoveAt(closestIndex);
565  if (!charactersInRange.Any(c => c.character == character))
566  {
567  charactersInRange.Add((character, nodes[parentNodeIndex]));
568  }
569  FindNodes(entitiesInRange, targetPos, nodes.Count - 1, currentRange);
570  }
571  }
572 
573  private void AddNodesBetweenPoints(Vector2 currPos, Vector2 targetPos, float variance, ref int parentNodeIndex)
574  {
575  Vector2 diff = targetPos - currPos;
576  float dist = diff.Length();
577  Vector2 normal = new Vector2(-diff.Y, diff.X) / dist;
578  for (float x = MaxNodeDistance; x < dist - MaxNodeDistance; x += MaxNodeDistance * Rand.Range(0.5f, 1.0f))
579  {
580  //0 at the edges, 1 at the center
581  float normalOffset = (0.5f - Math.Abs(x / dist - 0.5f)) * 2.0f;
582  normalOffset *= variance * dist * Rand.Range(-1.0f, 1.0f);
583 
584  nodes.Add(new Node(currPos + (diff / dist) * x + normal * normalOffset, parentNodeIndex));
585  parentNodeIndex = nodes.Count - 1;
586  }
587  }
588 
589  public override void ReceiveSignal(Signal signal, Connection connection)
590  {
591  switch (connection.Name)
592  {
593  case "activate":
594  case "use":
595  case "trigger_in":
596  if (signal.value != "0")
597  {
598  item.Use(1.0f);
599  }
600  break;
601  }
602  }
603 
604  protected override void RemoveComponentSpecific()
605  {
606  base.RemoveComponentSpecific();
607  list.Remove(this);
608  }
609 
610  public void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
611  {
612  msg.WriteUInt16(user?.ID ?? Entity.NullEntityID);
613  }
614  }
615 }
Attacks are used to deal damage to characters, structures and items. They can be defined in the weapo...
AttackResult ApplyAttack(Character attacker, Vector2 worldPosition, Attack attack, float deltaTime, Vector2 impulseDirection, bool playSound=false, Limb targetLimb=null, float penetration=0f)
Apply the specified attack to this character. If the targetLimb is not specified, the limb closest to...
virtual Vector2 WorldPosition
Definition: Entity.cs:49
Submarine Submarine
Definition: Entity.cs:53
const ushort NullEntityID
Definition: Entity.cs:14
static NetworkMember NetworkMember
Definition: GameMain.cs:190
static Hull FindHull(Vector2 position, Hull guess=null, bool useWorldCoordinates=true, bool inclusive=true)
Returns the hull which contains the point (or null if it isn't inside any)
void Use(float deltaTime, Character user=null, Limb targetLimb=null, Entity useTarget=null, Character userForOnUsedEvent=null)
override string Name
Note that this is not a LocalizedString instance, just the current name of the item as a string....
void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData=null)
override float GetCurrentPowerConsumption(Connection conn=null)
Discharge coil doesn't consume grid power, directly takes from the batteries on its grid instead.
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)
const float MaxOverVoltageFactor
Maximum voltage factor when the device is being overvolted. I.e. how many times more effectively the ...
float GetAvailableInstantaneousBatteryPower()
Returns the amount of power that can be supplied by batteries directly connected to the item
float BodyRotation
In radians, takes flipping into account
static bool RectContains(Rectangle rect, Vector2 pos, bool inclusive=false)
Rectangle? Borders
Extents of the solid items/structures (ones with a physics body) and hulls
Interface for entities that the server can send events to the clients
ActionType
ActionTypes define when a StatusEffect is executed.
Definition: Enums.cs:19
Node(Vector2 worldPosition, int parentIndex, float length=0.0f, float angle=0.0f)