Files
tbd-station-14/Content.Server/GameObjects/Components/Power/ApcNetComponents/PowerReceiverComponent.cs
metalgearsloth 3e64fd56a1 Physics (#3452)
* Content side new physics structure

* BroadPhase outline done

* But we need to fix WorldAABB

* Fix static pvs AABB

* Fix import

* Rando fixes

* B is for balloon

* Change human mob hitbox to circle

* Decent movement

* Start adding friction to player controller

I think it's the best way to go about it to keep other objects somewhat consistent for physics.

* This baby can fit so many physics bugs in it.

* Slight mob mover optimisations.

* Player mover kinda works okay.

* Beginnings of testbed

* More testbed

* Circlestack bed

* Namespaces

* BB fixes

* Pull WorldAABB

* Joint pulling

* Semi-decent movement I guess.

* Pulling better

* Bullet controller + old movement

* im too dumb for this shit

* Use kinematic mob controller again

It's probably for the best TBH

* Stashed shitcode

* Remove SlipController

* In which movement code is entirely refactored

* Singularity fix

* Fix ApplyLinearImpulse

* MoveRelay fix

* Fix door collisions

* Disable subfloor collisions

Saves on broadphase a fair bit

* Re-implement ClimbController

* Zumzum's pressure

* Laggy item throwing

* Minor atmos change

* Some caching

* Optimise controllers

* Optimise CollideWith to hell and back

* Re-do throwing and tile friction

* Landing too

* Optimise controllers

* Move CCVars and other stuff swept is beautiful

* Cleanup a bunch of controllers

* Fix shooting and high pressure movement controller

* Flashing improvements

* Stuff and things

* Combat collisions

* Combat mode collisions

* Pulling distance joint again

* Cleanup physics interfaces

* More like scuffedularity

* Shit's fucked

* Haha tests go green

* Bigmoneycrab

Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
2021-03-01 03:11:29 +11:00

255 lines
8.2 KiB
C#

#nullable enable
using System;
using Content.Server.GameObjects.Components.NodeContainer.NodeGroups;
using Content.Shared.GameObjects.Components.Power;
using Content.Shared.GameObjects.EntitySystems;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Physics;
using Robust.Shared.Serialization;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Power.ApcNetComponents
{
/// <summary>
/// Attempts to link with a nearby <see cref="IPowerProvider"/>s so that it can receive power from a <see cref="IApcNet"/>.
/// </summary>
[RegisterComponent]
public class PowerReceiverComponent : Component, IExamine
{
[Dependency] private readonly IServerEntityManager _serverEntityManager = default!;
[ViewVariables] [ComponentDependency] private readonly IPhysBody? _physicsComponent = null;
public override string Name => "PowerReceiver";
[ViewVariables]
public bool Powered => (HasApcPower || !NeedsPower) && !PowerDisabled;
/// <summary>
/// If this is being powered by an Apc.
/// </summary>
[ViewVariables]
public bool HasApcPower { get; private set; }
/// <summary>
/// The max distance from a <see cref="PowerProviderComponent"/> that this can receive power from.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
public int PowerReceptionRange { get => _powerReceptionRange; set => SetPowerReceptionRange(value); }
private int _powerReceptionRange;
[ViewVariables]
public IPowerProvider Provider { get => _provider; set => SetProvider(value); }
private IPowerProvider _provider = PowerProviderComponent.NullProvider;
/// <summary>
/// If this should be considered for connection by <see cref="PowerProviderComponent"/>s.
/// </summary>
public bool Connectable => Anchored;
private bool Anchored => _physicsComponent == null || _physicsComponent.BodyType == BodyType.Static;
[ViewVariables]
public bool NeedsProvider { get; private set; } = true;
/// <summary>
/// Amount of charge this needs from an APC per second to function.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
public int Load { get => _load; set => SetLoad(value); }
private int _load;
/// <summary>
/// When false, causes this to appear powered even if not receiving power from an Apc.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
public bool NeedsPower { get => _needsPower; set => SetNeedsPower(value); }
private bool _needsPower;
/// <summary>
/// When true, causes this to never appear powered.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
public bool PowerDisabled { get => _powerDisabled; set => SetPowerDisabled(value); }
private bool _powerDisabled;
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(ref _powerReceptionRange, "powerReceptionRange", 3);
serializer.DataField(ref _load, "powerLoad", 5);
serializer.DataField(ref _needsPower, "needsPower", true);
serializer.DataField(ref _powerDisabled, "powerDisabled", false);
}
protected override void Startup()
{
base.Startup();
if (NeedsProvider)
{
TryFindAndSetProvider();
}
if (_physicsComponent != null)
{
AnchorUpdate();
}
}
public override void OnRemove()
{
_provider.RemoveReceiver(this);
base.OnRemove();
}
public void TryFindAndSetProvider()
{
if (TryFindAvailableProvider(out var provider))
{
Provider = provider;
}
}
public override void HandleMessage(ComponentMessage message, IComponent? component)
{
base.HandleMessage(message, component);
switch (message)
{
case AnchoredChangedMessage:
AnchorUpdate();
break;
}
}
public void ApcPowerChanged()
{
var oldPowered = Powered;
HasApcPower = Provider.HasApcPower;
if (Powered != oldPowered)
OnNewPowerState();
}
private bool TryFindAvailableProvider(out IPowerProvider foundProvider)
{
var nearbyEntities = _serverEntityManager
.GetEntitiesInRange(Owner, PowerReceptionRange);
foreach (var entity in nearbyEntities)
{
if (entity.TryGetComponent<PowerProviderComponent>(out var provider))
{
if (provider.Connectable)
{
if (provider.Owner.Transform.Coordinates.TryDistance(_serverEntityManager, Owner.Transform.Coordinates, out var distance))
{
if (distance < Math.Min(PowerReceptionRange, provider.PowerTransferRange))
{
foundProvider = provider;
return true;
}
}
}
}
}
foundProvider = default!;
return false;
}
public void ClearProvider()
{
_provider.RemoveReceiver(this);
_provider = PowerProviderComponent.NullProvider;
NeedsProvider = true;
ApcPowerChanged();
}
private void SetProvider(IPowerProvider newProvider)
{
_provider.RemoveReceiver(this);
_provider = newProvider;
newProvider.AddReceiver(this);
NeedsProvider = false;
ApcPowerChanged();
}
private void SetPowerReceptionRange(int newPowerReceptionRange)
{
ClearProvider();
_powerReceptionRange = newPowerReceptionRange;
TryFindAndSetProvider();
}
private void SetLoad(int newLoad)
{
Provider.UpdateReceiverLoad(Load, newLoad);
_load = newLoad;
}
private void SetNeedsPower(bool newNeedsPower)
{
var oldPowered = Powered;
_needsPower = newNeedsPower;
if (oldPowered != Powered)
{
OnNewPowerState();
}
}
private void SetPowerDisabled(bool newPowerDisabled)
{
var oldPowered = Powered;
_powerDisabled = newPowerDisabled;
if (oldPowered != Powered)
{
OnNewPowerState();
}
}
private void OnNewPowerState()
{
SendMessage(new PowerChangedMessage(Powered));
if (Owner.TryGetComponent<AppearanceComponent>(out var appearance))
{
appearance.SetData(PowerDeviceVisuals.Powered, Powered);
}
}
private void AnchorUpdate()
{
if (Anchored)
{
if (NeedsProvider)
{
TryFindAndSetProvider();
}
}
else
{
ClearProvider();
}
}
///<summary>
///Adds some markup to the examine text of whatever object is using this component to tell you if it's powered or not, even if it doesn't have an icon state to do this for you.
///</summary>
public void Examine(FormattedMessage message, bool inDetailsRange)
{
message.AddMarkup(Loc.GetString("It appears to be {0}.", Powered ? "[color=darkgreen]powered[/color]" : "[color=darkred]un-powered[/color]"));
}
}
public class PowerChangedMessage : ComponentMessage
{
public readonly bool Powered;
public PowerChangedMessage(bool powered)
{
Powered = powered;
}
}
}