4 using Microsoft.Xna.Framework;
6 using System.Collections.Generic;
13 private static readonly List<ElectricalDischarger> list =
new List<ElectricalDischarger>();
14 public static IEnumerable<ElectricalDischarger>
List
19 const int MaxNodes = 100;
20 const float MaxNodeDistance = 150.0f;
29 public Node(Vector2 worldPosition,
int parentIndex,
float length = 0.0f,
float angle = 0.0f)
40 get {
return base.IsActive; }
43 base.IsActive = value;
47 charactersInRange.Clear();
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)]
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)]
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)]
97 private readonly List<Node> nodes =
new List<Node>();
100 get {
return nodes; }
105 private bool charging;
109 private readonly
Attack attack;
113 private float reloadTimer;
120 foreach (var subElement
in element.Elements())
122 switch (subElement.Name.ToString().ToLowerInvariant())
133 partial
void InitProjSpecific();
155 frameOffset = Rand.Int(electricitySprite.FrameCount);
159 if (reloadTimer > 0.0f)
161 reloadTimer -= deltaTime;
171 bool hasPower =
false;
186 while (neededPower > 0.0001f && batteries.Any())
188 float takePower = neededPower / batteries.Count();
189 takePower = Math.Min(takePower, batteries.Min(b => Math.Min(b.Charge * 3600.0f, b.MaxOutPut)));
192 neededPower -= takePower;
193 battery.
Charge -= takePower / 3600.0f;
214 base.UpdateBroken(deltaTime, cam);
216 charactersInRange.Clear();
219 private void Discharge()
226 foreach ((
Character character, Node node) in charactersInRange)
228 if (character ==
null || character.
Removed) {
continue; }
230 impulseDirection: character.
WorldPosition - node.WorldPosition);
233 DischargeProjSpecific();
237 partial
void DischargeProjSpecific();
239 public void FindNodes(Vector2 worldPosition,
float range)
250 worldPosition +=
new Vector2((
float)Math.Cos(angle), (
float)Math.Sin(angle)) *
RaycastRange * dir;
254 List<Submarine> submarinesInRange =
new List<Submarine>();
259 submarinesInRange.Add(sub);
261 else if (sub !=
null)
263 Rectangle subBorders =
new Rectangle(
265 sub.
Borders.Width + (
int)(range * 2), sub.
Borders.Height + (
int)(range * 2));
269 submarinesInRange.Add(sub);
275 List<Entity> entitiesInRange =
new List<Entity>(100);
279 if (structure.
Submarine !=
null&& !submarinesInRange.Contains(structure.
Submarine)) {
continue; }
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; }
289 if (!submarinesInRange.Contains(structure.
Submarine)) {
continue; }
293 Vector2 normal =
new Vector2(
296 Vector2 structurePos = structure.
Position;
300 structurePos = ConvertUnits.ToDisplayUnits(structure.
Bodies.First().Position);
301 offsetAmount = Math.Max(
305 if (
Hull.
FindHull(structurePos + normal * offsetAmount, useWorldCoordinates:
false) !=
null &&
306 Hull.
FindHull(structurePos - normal * offsetAmount, useWorldCoordinates:
false) !=
null)
313 entitiesInRange.Add(structure);
321 int parentNodeIndex = 0;
322 AddNodesBetweenPoints(
item.
WorldPosition, worldPosition, 0.5f, ref parentNodeIndex);
326 nodes.Add(
new Node(worldPosition, -1));
333 if (!character.
Enabled) {
continue; }
334 if (
IgnoreUser && character == user) {
continue; }
336 if (character.
Submarine !=
null && !submarinesInRange.Contains(character.
Submarine)) {
continue; }
340 entitiesInRange.Add(character);
349 if (!entitiesInRange.Contains(character)) { entitiesInRange.Add(character); }
350 charactersInRange.Add((character, nodes.First()));
355 FindNodes(entitiesInRange, worldPosition, nodes.Count - 1, range);
358 for (
int i = 0; i < nodes.Count; i++)
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);
368 private void FindNodes(List<Entity> entitiesInRange, Vector2 currPos,
int parentNodeIndex,
float currentRange)
370 if (currentRange <= 0.0f || nodes.Count >= MaxNodes) {
return; }
373 int closestIndex = -1;
374 float closestDist =
float.MaxValue;
375 for (
int i = 0; i < entitiesInRange.Count; i++)
377 float dist =
float.MaxValue;
378 if (entitiesInRange[i] is
Structure structure)
380 if (structure.IsHorizontal)
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;
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;
397 else if (entitiesInRange[i] is Character character)
399 dist = MathF.Sqrt(MathUtils.LineSegmentToPointDistanceSquared(currPos, nodes[parentNodeIndex].WorldPosition, character.WorldPosition));
402 if (dist < closestDist)
409 if (closestIndex == -1 || closestDist > currentRange)
412 for (
int i = 0; i < Rand.Int(4); i++)
414 Vector2 targetPos = currPos + Rand.Vector(MaxNodeDistance * Rand.Range(0.5f, 1.5f));
415 nodes.Add(
new Node(targetPos, parentNodeIndex));
419 currentRange -= closestDist;
421 if (entitiesInRange[closestIndex] is Structure targetStructure)
423 if (targetStructure.IsHorizontal)
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);
432 int sectionIndex = targetStructure.FindSectionIndex(currPos, world:
true, clamp:
true);
433 if (sectionIndex == -1) {
return; }
434 Vector2 sectionPos = targetStructure.SectionPosition(sectionIndex, world:
true);
437 MathHelper.Clamp(sectionPos.X, targetStructure.WorldRect.X, targetStructure.WorldRect.Right),
438 sectionPos.Y + targetStructure.BodyHeight / 2 * yDir);
441 AddNodesBetweenPoints(currPos, targetPos, 0.25f, ref parentNodeIndex);
444 nodes.Add(
new Node(targetPos, parentNodeIndex));
445 int nodeIndex = nodes.Count - 1;
446 entitiesInRange.RemoveAt(closestIndex);
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);
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);
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);
472 int sectionIndex = targetStructure.FindSectionIndex(currPos, world:
true, clamp:
true);
473 if (sectionIndex == -1) {
return; }
474 Vector2 sectionPos = targetStructure.SectionPosition(sectionIndex, world:
true);
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));
481 AddNodesBetweenPoints(currPos, targetPos, 0.25f, ref parentNodeIndex);
484 nodes.Add(
new Node(targetPos, parentNodeIndex));
485 int nodeIndex = nodes.Count - 1;
486 entitiesInRange.RemoveAt(closestIndex);
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);
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);
508 for (
int j = 0; j < entitiesInRange.Count; j++)
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; }
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)
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)
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)
537 if (!charactersInRange.Any(c => c.character == character))
539 charactersInRange.Add((character, nodes[parentNodeIndex]));
541 float closestNodeDistSqr =
float.MaxValue;
542 int closestNodeIndex = -1;
543 for (
int i = 0; i < nodes.Count; i++)
545 float distSqr = Vector2.DistanceSquared(character.WorldPosition, nodes[i].WorldPosition);
546 if (distSqr < closestNodeDistSqr)
548 closestNodeDistSqr = distSqr;
549 closestNodeIndex = i;
552 if (closestNodeIndex > -1)
554 FindNodes(entitiesInRange, nodes[closestNodeIndex].WorldPosition, closestNodeIndex, currentRange - (
float)Math.Sqrt(closestNodeDistSqr));
558 else if (entitiesInRange[closestIndex] is Character character)
560 Vector2 targetPos = character.WorldPosition;
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))
567 charactersInRange.Add((character, nodes[parentNodeIndex]));
569 FindNodes(entitiesInRange, targetPos, nodes.Count - 1, currentRange);
573 private void AddNodesBetweenPoints(Vector2 currPos, Vector2 targetPos,
float variance, ref
int parentNodeIndex)
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))
581 float normalOffset = (0.5f - Math.Abs(x / dist - 0.5f)) * 2.0f;
582 normalOffset *= variance * dist * Rand.Range(-1.0f, 1.0f);
584 nodes.Add(
new Node(currPos + (diff / dist) * x + normal * normalOffset, parentNodeIndex));
585 parentNodeIndex = nodes.Count - 1;
591 switch (connection.
Name)
596 if (signal.
value !=
"0")
606 base.RemoveComponentSpecific();
Attacks are used to deal damage to characters, structures and items. They can be defined in the weapo...
static readonly List< Character > CharacterList
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
const ushort NullEntityID
static NetworkMember NetworkMember
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)
List< Connection > Connections
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)
float RangeMultiplierInWalls
static IEnumerable< ElectricalDischarger > List
IEnumerable< Node > Nodes
override void Update(float deltaTime, Camera cam)
override void UpdateBroken(float deltaTime, Camera cam)
override void ReceiveSignal(Signal signal, Connection connection)
override void RemoveComponentSpecific()
ElectricalDischarger(Item item, ContentXElement element)
void FindNodes(Vector2 worldPosition, float range)
override bool Use(float deltaTime, Character character=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 ...
IEnumerable< PowerContainer > GetDirectlyConnectedBatteries()
float GetAvailableInstantaneousBatteryPower()
Returns the amount of power that can be supplied by batteries directly connected to the item
override Vector2 Position
float BodyRotation
In radians, takes flipping into account
static List< Structure > WallList
static bool RectContains(Rectangle rect, Vector2 pos, bool inclusive=false)
static List< Submarine > Loaded
static readonly Vector2 GridSize
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
void WriteUInt16(UInt16 val)
ActionType
ActionTypes define when a StatusEffect is executed.
Node(Vector2 worldPosition, int parentIndex, float length=0.0f, float angle=0.0f)