| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318 |
- using Content.Server.Actions;
- using Content.Server.Administration.Logs;
- using Content.Server.Administration.Managers;
- using Content.Server.DeviceNetwork.Systems;
- using Content.Server.Explosion.EntitySystems;
- using Content.Server.Hands.Systems;
- using Content.Server.PowerCell;
- using Content.Shared.Alert;
- using Content.Shared.Database;
- using Content.Shared.IdentityManagement;
- using Content.Shared.Interaction;
- using Content.Shared.Item.ItemToggle.Components;
- using Content.Shared.Mind;
- using Content.Shared.Mind.Components;
- using Content.Shared.Mobs;
- using Content.Shared.Mobs.Systems;
- using Content.Shared.Movement.Systems;
- using Content.Shared.Pointing;
- using Content.Shared.PowerCell;
- using Content.Shared.PowerCell.Components;
- using Content.Shared.Roles;
- using Content.Shared.Silicons.Borgs;
- using Content.Shared.Silicons.Borgs.Components;
- using Content.Shared.Throwing;
- using Content.Shared.Whitelist;
- using Content.Shared.Wires;
- using Robust.Server.GameObjects;
- using Robust.Shared.Containers;
- using Robust.Shared.Player;
- using Robust.Shared.Random;
- using Robust.Shared.Timing;
- namespace Content.Server.Silicons.Borgs;
- /// <inheritdoc/>
- public sealed partial class BorgSystem : SharedBorgSystem
- {
- [Dependency] private readonly IAdminLogManager _adminLog = default!;
- [Dependency] private readonly IBanManager _banManager = default!;
- [Dependency] private readonly IGameTiming _timing = default!;
- [Dependency] private readonly IRobustRandom _random = default!;
- [Dependency] private readonly ActionsSystem _actions = default!;
- [Dependency] private readonly AlertsSystem _alerts = default!;
- [Dependency] private readonly DeviceNetworkSystem _deviceNetwork = default!;
- [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
- [Dependency] private readonly TriggerSystem _trigger = default!;
- [Dependency] private readonly HandsSystem _hands = default!;
- [Dependency] private readonly MetaDataSystem _metaData = default!;
- [Dependency] private readonly SharedMindSystem _mind = default!;
- [Dependency] private readonly MobStateSystem _mobState = default!;
- [Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!;
- [Dependency] private readonly PowerCellSystem _powerCell = default!;
- [Dependency] private readonly ThrowingSystem _throwing = default!;
- [Dependency] private readonly UserInterfaceSystem _ui = default!;
- [Dependency] private readonly SharedContainerSystem _container = default!;
- [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
- /// <inheritdoc/>
- public override void Initialize()
- {
- base.Initialize();
- SubscribeLocalEvent<BorgChassisComponent, MapInitEvent>(OnMapInit);
- SubscribeLocalEvent<BorgChassisComponent, AfterInteractUsingEvent>(OnChassisInteractUsing);
- SubscribeLocalEvent<BorgChassisComponent, MindAddedMessage>(OnMindAdded);
- SubscribeLocalEvent<BorgChassisComponent, MindRemovedMessage>(OnMindRemoved);
- SubscribeLocalEvent<BorgChassisComponent, MobStateChangedEvent>(OnMobStateChanged);
- SubscribeLocalEvent<BorgChassisComponent, PowerCellChangedEvent>(OnPowerCellChanged);
- SubscribeLocalEvent<BorgChassisComponent, PowerCellSlotEmptyEvent>(OnPowerCellSlotEmpty);
- SubscribeLocalEvent<BorgChassisComponent, GetCharactedDeadIcEvent>(OnGetDeadIC);
- SubscribeLocalEvent<BorgChassisComponent, ItemToggledEvent>(OnToggled);
- SubscribeLocalEvent<BorgBrainComponent, MindAddedMessage>(OnBrainMindAdded);
- SubscribeLocalEvent<BorgBrainComponent, PointAttemptEvent>(OnBrainPointAttempt);
- InitializeModules();
- InitializeMMI();
- InitializeUI();
- InitializeTransponder();
- }
- private void OnMapInit(EntityUid uid, BorgChassisComponent component, MapInitEvent args)
- {
- UpdateBatteryAlert((uid, component));
- _movementSpeedModifier.RefreshMovementSpeedModifiers(uid);
- }
- private void OnChassisInteractUsing(EntityUid uid, BorgChassisComponent component, AfterInteractUsingEvent args)
- {
- if (!args.CanReach || args.Handled || uid == args.User)
- return;
- var used = args.Used;
- TryComp<BorgBrainComponent>(used, out var brain);
- TryComp<BorgModuleComponent>(used, out var module);
- if (TryComp<WiresPanelComponent>(uid, out var panel) && !panel.Open)
- {
- if (brain != null || module != null)
- {
- Popup.PopupEntity(Loc.GetString("borg-panel-not-open"), uid, args.User);
- }
- return;
- }
- if (component.BrainEntity == null && brain != null &&
- _whitelistSystem.IsWhitelistPassOrNull(component.BrainWhitelist, used))
- {
- if (_mind.TryGetMind(used, out _, out var mind) && mind.Session != null)
- {
- if (!CanPlayerBeBorged(mind.Session))
- {
- Popup.PopupEntity(Loc.GetString("borg-player-not-allowed"), used, args.User);
- return;
- }
- }
- _container.Insert(used, component.BrainContainer);
- _adminLog.Add(LogType.Action, LogImpact.Medium,
- $"{ToPrettyString(args.User):player} installed brain {ToPrettyString(used)} into borg {ToPrettyString(uid)}");
- args.Handled = true;
- UpdateUI(uid, component);
- }
- if (module != null && CanInsertModule(uid, used, component, module, args.User))
- {
- InsertModule((uid, component), used);
- _adminLog.Add(LogType.Action, LogImpact.Low,
- $"{ToPrettyString(args.User):player} installed module {ToPrettyString(used)} into borg {ToPrettyString(uid)}");
- args.Handled = true;
- UpdateUI(uid, component);
- }
- }
- /// <summary>
- /// Inserts a new module into a borg, the same as if a player inserted it manually.
- /// </summary>
- /// <para>
- /// This does not run checks to see if the borg is actually allowed to be inserted, such as whitelists.
- /// </para>
- /// <param name="ent">The borg to insert into.</param>
- /// <param name="module">The module to insert.</param>
- public void InsertModule(Entity<BorgChassisComponent> ent, EntityUid module)
- {
- _container.Insert(module, ent.Comp.ModuleContainer);
- }
- // todo: consider transferring over the ghost role? managing that might suck.
- protected override void OnInserted(EntityUid uid, BorgChassisComponent component, EntInsertedIntoContainerMessage args)
- {
- base.OnInserted(uid, component, args);
- if (HasComp<BorgBrainComponent>(args.Entity) && _mind.TryGetMind(args.Entity, out var mindId, out var mind) && args.Container == component.BrainContainer)
- {
- _mind.TransferTo(mindId, uid, mind: mind);
- }
- }
- protected override void OnRemoved(EntityUid uid, BorgChassisComponent component, EntRemovedFromContainerMessage args)
- {
- base.OnRemoved(uid, component, args);
- if (HasComp<BorgBrainComponent>(args.Entity) && _mind.TryGetMind(uid, out var mindId, out var mind) && args.Container == component.BrainContainer)
- {
- _mind.TransferTo(mindId, args.Entity, mind: mind);
- }
- }
- private void OnMindAdded(EntityUid uid, BorgChassisComponent component, MindAddedMessage args)
- {
- BorgActivate(uid, component);
- }
- private void OnMindRemoved(EntityUid uid, BorgChassisComponent component, MindRemovedMessage args)
- {
- BorgDeactivate(uid, component);
- }
- private void OnMobStateChanged(EntityUid uid, BorgChassisComponent component, MobStateChangedEvent args)
- {
- if (args.NewMobState == MobState.Alive)
- {
- if (_mind.TryGetMind(uid, out _, out _))
- _powerCell.SetDrawEnabled(uid, true);
- }
- else
- {
- _powerCell.SetDrawEnabled(uid, false);
- }
- }
- private void OnPowerCellChanged(EntityUid uid, BorgChassisComponent component, PowerCellChangedEvent args)
- {
- UpdateBatteryAlert((uid, component));
- // if we aren't drawing and suddenly get enough power to draw again, reeanble.
- if (_powerCell.HasDrawCharge(uid))
- {
- Toggle.TryActivate(uid);
- }
- UpdateUI(uid, component);
- }
- private void OnPowerCellSlotEmpty(EntityUid uid, BorgChassisComponent component, ref PowerCellSlotEmptyEvent args)
- {
- Toggle.TryDeactivate(uid);
- UpdateUI(uid, component);
- }
- private void OnGetDeadIC(EntityUid uid, BorgChassisComponent component, ref GetCharactedDeadIcEvent args)
- {
- args.Dead = true;
- }
- private void OnToggled(Entity<BorgChassisComponent> ent, ref ItemToggledEvent args)
- {
- var (uid, comp) = ent;
- if (args.Activated)
- InstallAllModules(uid, comp);
- else
- DisableAllModules(uid, comp);
- // only enable the powerdraw if there is a player in the chassis
- var drawing = _mind.TryGetMind(uid, out _, out _) && _mobState.IsAlive(ent);
- _powerCell.SetDrawEnabled(uid, drawing);
- UpdateUI(uid, comp);
- _movementSpeedModifier.RefreshMovementSpeedModifiers(uid);
- }
- private void OnBrainMindAdded(EntityUid uid, BorgBrainComponent component, MindAddedMessage args)
- {
- if (!Container.TryGetOuterContainer(uid, Transform(uid), out var container))
- return;
- var containerEnt = container.Owner;
- if (!TryComp<BorgChassisComponent>(containerEnt, out var chassisComponent) ||
- container.ID != chassisComponent.BrainContainerId)
- return;
- if (!_mind.TryGetMind(uid, out var mindId, out var mind) || mind.Session == null)
- return;
- if (!CanPlayerBeBorged(mind.Session))
- {
- Popup.PopupEntity(Loc.GetString("borg-player-not-allowed-eject"), uid);
- Container.RemoveEntity(containerEnt, uid);
- _throwing.TryThrow(uid, _random.NextVector2() * 5, 5f);
- return;
- }
- _mind.TransferTo(mindId, containerEnt, mind: mind);
- }
- private void OnBrainPointAttempt(EntityUid uid, BorgBrainComponent component, PointAttemptEvent args)
- {
- args.Cancel();
- }
- private void UpdateBatteryAlert(Entity<BorgChassisComponent> ent, PowerCellSlotComponent? slotComponent = null)
- {
- if (!_powerCell.TryGetBatteryFromSlot(ent, out var battery, slotComponent))
- {
- _alerts.ClearAlert(ent, ent.Comp.BatteryAlert);
- _alerts.ShowAlert(ent, ent.Comp.NoBatteryAlert);
- return;
- }
- var chargePercent = (short) MathF.Round(battery.CurrentCharge / battery.MaxCharge * 10f);
- // we make sure 0 only shows if they have absolutely no battery.
- // also account for floating point imprecision
- if (chargePercent == 0 && _powerCell.HasDrawCharge(ent, cell: slotComponent))
- {
- chargePercent = 1;
- }
- _alerts.ClearAlert(ent, ent.Comp.NoBatteryAlert);
- _alerts.ShowAlert(ent, ent.Comp.BatteryAlert, chargePercent);
- }
- /// <summary>
- /// Activates a borg when a player occupies it
- /// </summary>
- public void BorgActivate(EntityUid uid, BorgChassisComponent component)
- {
- Popup.PopupEntity(Loc.GetString("borg-mind-added", ("name", Identity.Name(uid, EntityManager))), uid);
- if (_powerCell.HasDrawCharge(uid))
- {
- Toggle.TryActivate(uid);
- _powerCell.SetDrawEnabled(uid, _mobState.IsAlive(uid));
- }
- _appearance.SetData(uid, BorgVisuals.HasPlayer, true);
- }
- /// <summary>
- /// Deactivates a borg when a player leaves it.
- /// </summary>
- public void BorgDeactivate(EntityUid uid, BorgChassisComponent component)
- {
- Popup.PopupEntity(Loc.GetString("borg-mind-removed", ("name", Identity.Name(uid, EntityManager))), uid);
- Toggle.TryDeactivate(uid);
- _powerCell.SetDrawEnabled(uid, false);
- _appearance.SetData(uid, BorgVisuals.HasPlayer, false);
- }
- /// <summary>
- /// Checks that a player has fulfilled the requirements for the borg job.
- /// If they don't have enough hours, they cannot be placed into a chassis.
- /// </summary>
- public bool CanPlayerBeBorged(ICommonSession session)
- {
- return true;
- }
- }
|