# Conflicts: # Content.Server/GameTicking/GameTicker.Spawning.cs # Content.Server/Players/PlayTimeTracking/PlayTimeTrackingSystem.cs # Content.Server/Roles/RoleSystem.cs # Content.Server/Silicons/Laws/SiliconLawSystem.cs # Content.Server/Station/Systems/StationSpawningSystem.cs # Resources/Prototypes/Entities/Objects/Fun/pai.yml # Resources/Prototypes/GameRules/midround.yml # RobustToolbox
352 lines
13 KiB
C#
352 lines
13 KiB
C#
using Content.Server.AlertLevel;
|
||
using Content.Server.CartridgeLoader;
|
||
using Content.Server.Chat.Managers;
|
||
using Content.Server.DeviceNetwork.Components;
|
||
using Content.Server.Instruments;
|
||
using Content.Server.Light.EntitySystems;
|
||
using Content.Server.PDA.Ringer;
|
||
using Content.Server.RoundEnd;
|
||
using Content.Server.Shuttles.Components;
|
||
using Content.Server.Shuttles.Systems;
|
||
using Content.Server.Station.Systems;
|
||
using Content.Server.Store.Components;
|
||
using Content.Server.Store.Systems;
|
||
using Content.Server.Traitor.Uplink;
|
||
using Content.Shared.Access.Components;
|
||
using Content.Shared.CartridgeLoader;
|
||
using Content.Shared.Chat;
|
||
using Content.Shared.Light;
|
||
using Content.Shared.Light.Components;
|
||
using Content.Shared.Light.EntitySystems;
|
||
using Content.Shared.PDA;
|
||
using Content.Shared.Store.Components;
|
||
using Robust.Server.Containers;
|
||
using Robust.Server.GameObjects;
|
||
using Robust.Shared.Configuration;
|
||
using Robust.Shared.Containers;
|
||
using Robust.Shared.Player;
|
||
using Robust.Shared.Utility;
|
||
using Content.Shared.CCVar;
|
||
using Robust.Shared.Timing;
|
||
|
||
namespace Content.Server.PDA
|
||
{
|
||
public sealed class PdaSystem : SharedPdaSystem
|
||
{
|
||
[Dependency] private readonly CartridgeLoaderSystem _cartridgeLoader = default!;
|
||
[Dependency] private readonly InstrumentSystem _instrument = default!;
|
||
[Dependency] private readonly RingerSystem _ringer = default!;
|
||
[Dependency] private readonly StationSystem _station = default!;
|
||
[Dependency] private readonly StoreSystem _store = default!;
|
||
[Dependency] private readonly IChatManager _chatManager = default!;
|
||
[Dependency] private readonly UserInterfaceSystem _ui = default!;
|
||
[Dependency] private readonly UnpoweredFlashlightSystem _unpoweredFlashlight = default!;
|
||
[Dependency] private readonly ContainerSystem _containerSystem = default!;
|
||
[Dependency] private readonly RoundEndSystem _roundEndSystem = default!;
|
||
[Dependency] private readonly EmergencyShuttleSystem _emergencyShuttleSystem = default!;
|
||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||
|
||
public override void Initialize()
|
||
{
|
||
base.Initialize();
|
||
|
||
SubscribeLocalEvent<PdaComponent, LightToggleEvent>(OnLightToggle);
|
||
|
||
// UI Events:
|
||
SubscribeLocalEvent<PdaComponent, BoundUIOpenedEvent>(OnPdaOpen);
|
||
SubscribeLocalEvent<PdaComponent, PdaRequestUpdateInterfaceMessage>(OnUiMessage);
|
||
SubscribeLocalEvent<PdaComponent, PdaToggleFlashlightMessage>(OnUiMessage);
|
||
SubscribeLocalEvent<PdaComponent, PdaShowRingtoneMessage>(OnUiMessage);
|
||
SubscribeLocalEvent<PdaComponent, PdaShowMusicMessage>(OnUiMessage);
|
||
SubscribeLocalEvent<PdaComponent, PdaShowUplinkMessage>(OnUiMessage);
|
||
SubscribeLocalEvent<PdaComponent, PdaLockUplinkMessage>(OnUiMessage);
|
||
|
||
SubscribeLocalEvent<PdaComponent, CartridgeLoaderNotificationSentEvent>(OnNotification);
|
||
|
||
SubscribeLocalEvent<StationRenamedEvent>(OnStationRenamed);
|
||
SubscribeLocalEvent<EntityRenamedEvent>(OnEntityRenamed);
|
||
SubscribeLocalEvent<AlertLevelChangedEvent>(OnAlertLevelChanged);
|
||
}
|
||
|
||
private void OnEntityRenamed(ref EntityRenamedEvent ev)
|
||
{
|
||
var query = EntityQueryEnumerator<PdaComponent>();
|
||
|
||
while (query.MoveNext(out var uid, out var comp))
|
||
{
|
||
if (comp.PdaOwner == ev.Uid)
|
||
{
|
||
SetOwner(uid, comp, ev.Uid, ev.NewName);
|
||
}
|
||
}
|
||
}
|
||
|
||
protected override void OnComponentInit(EntityUid uid, PdaComponent pda, ComponentInit args)
|
||
{
|
||
base.OnComponentInit(uid, pda, args);
|
||
|
||
if (!HasComp<UserInterfaceComponent>(uid))
|
||
return;
|
||
|
||
UpdateAlertLevel(uid, pda);
|
||
UpdateStationName(uid, pda);
|
||
UpdateEvacShuttle(uid, pda); // Sunrise-edit
|
||
}
|
||
|
||
protected override void OnItemInserted(EntityUid uid, PdaComponent pda, EntInsertedIntoContainerMessage args)
|
||
{
|
||
base.OnItemInserted(uid, pda, args);
|
||
UpdatePdaUi(uid, pda);
|
||
}
|
||
|
||
protected override void OnItemRemoved(EntityUid uid, PdaComponent pda, EntRemovedFromContainerMessage args)
|
||
{
|
||
if (args.Container.ID != pda.IdSlot.ID && args.Container.ID != pda.PenSlot.ID && args.Container.ID != pda.PaiSlot.ID)
|
||
return;
|
||
|
||
// TODO: This is super cursed just use compstates please.
|
||
if (MetaData(uid).EntityLifeStage >= EntityLifeStage.Terminating)
|
||
return;
|
||
|
||
base.OnItemRemoved(uid, pda, args);
|
||
UpdatePdaUi(uid, pda);
|
||
}
|
||
|
||
private void OnLightToggle(EntityUid uid, PdaComponent pda, LightToggleEvent args)
|
||
{
|
||
pda.FlashlightOn = args.IsOn;
|
||
UpdatePdaUi(uid, pda);
|
||
}
|
||
|
||
public void SetOwner(EntityUid uid, PdaComponent pda, EntityUid owner, string ownerName)
|
||
{
|
||
pda.OwnerName = ownerName;
|
||
pda.PdaOwner = owner;
|
||
UpdatePdaUi(uid, pda);
|
||
}
|
||
|
||
private void OnStationRenamed(StationRenamedEvent ev)
|
||
{
|
||
UpdateAllPdaUisOnStation();
|
||
}
|
||
|
||
private void OnAlertLevelChanged(AlertLevelChangedEvent args)
|
||
{
|
||
UpdateAllPdaUisOnStation();
|
||
}
|
||
|
||
private void UpdateAllPdaUisOnStation()
|
||
{
|
||
var query = AllEntityQuery<PdaComponent>();
|
||
while (query.MoveNext(out var ent, out var comp))
|
||
{
|
||
UpdatePdaUi(ent, comp);
|
||
}
|
||
}
|
||
|
||
private void OnNotification(Entity<PdaComponent> ent, ref CartridgeLoaderNotificationSentEvent args)
|
||
{
|
||
_ringer.RingerPlayRingtone(ent.Owner);
|
||
|
||
if (!_containerSystem.TryGetContainingContainer((ent, null, null), out var container)
|
||
|| !TryComp<ActorComponent>(container.Owner, out var actor))
|
||
return;
|
||
|
||
var message = FormattedMessage.EscapeText(args.Message);
|
||
var wrappedMessage = Loc.GetString("pda-notification-message",
|
||
("header", args.Header),
|
||
("message", message));
|
||
|
||
_chatManager.ChatMessageToOne(
|
||
ChatChannel.Notifications,
|
||
message,
|
||
wrappedMessage,
|
||
EntityUid.Invalid,
|
||
false,
|
||
actor.PlayerSession.Channel);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Send new UI state to clients, call if you modify something like uplink.
|
||
/// </summary>
|
||
public void UpdatePdaUi(EntityUid uid, PdaComponent? pda = null)
|
||
{
|
||
if (!Resolve(uid, ref pda, false))
|
||
return;
|
||
|
||
if (!_ui.HasUi(uid, PdaUiKey.Key))
|
||
return;
|
||
|
||
var address = GetDeviceNetAddress(uid);
|
||
var hasInstrument = HasComp<InstrumentComponent>(uid);
|
||
var showUplink = HasComp<UplinkComponent>(uid) && IsUnlocked(uid);
|
||
|
||
UpdateStationName(uid, pda);
|
||
UpdateAlertLevel(uid, pda);
|
||
UpdateEvacShuttle(uid, pda); // Sunrise-edit
|
||
// TODO: Update the level and name of the station with each call to UpdatePdaUi is only needed for latejoin players.
|
||
// TODO: If someone can implement changing the level and name of the station when changing the PDA grid, this can be removed.
|
||
|
||
// TODO don't make this depend on cartridge loader!?!?
|
||
if (!TryComp(uid, out CartridgeLoaderComponent? loader))
|
||
return;
|
||
|
||
var programs = _cartridgeLoader.GetAvailablePrograms(uid, loader);
|
||
var id = CompOrNull<IdCardComponent>(pda.ContainedId);
|
||
var state = new PdaUpdateState(
|
||
programs,
|
||
GetNetEntity(loader.ActiveProgram),
|
||
pda.FlashlightOn,
|
||
pda.PenSlot.HasItem,
|
||
pda.PaiSlot.HasItem,
|
||
new PdaIdInfoText
|
||
{
|
||
ActualOwnerName = pda.OwnerName,
|
||
IdOwner = id?.FullName,
|
||
JobTitle = id?.LocalizedJobTitle,
|
||
StationAlertLevel = pda.StationAlertLevel,
|
||
StationAlertColor = pda.StationAlertColor,
|
||
EvacShuttleStatus = pda.ShuttleStatus, // Sunrise-edit
|
||
EvacShuttleTime = pda.ShuttleTime // Sunrise-edit
|
||
},
|
||
pda.StationName,
|
||
showUplink,
|
||
hasInstrument,
|
||
address);
|
||
|
||
_ui.SetUiState(uid, PdaUiKey.Key, state);
|
||
}
|
||
|
||
private void OnPdaOpen(Entity<PdaComponent> ent, ref BoundUIOpenedEvent args)
|
||
{
|
||
if (!PdaUiKey.Key.Equals(args.UiKey))
|
||
return;
|
||
|
||
UpdatePdaUi(ent.Owner, ent.Comp);
|
||
}
|
||
|
||
private void OnUiMessage(EntityUid uid, PdaComponent pda, PdaRequestUpdateInterfaceMessage msg)
|
||
{
|
||
if (!PdaUiKey.Key.Equals(msg.UiKey))
|
||
return;
|
||
|
||
UpdatePdaUi(uid, pda);
|
||
}
|
||
|
||
private void OnUiMessage(EntityUid uid, PdaComponent pda, PdaToggleFlashlightMessage msg)
|
||
{
|
||
if (!PdaUiKey.Key.Equals(msg.UiKey))
|
||
return;
|
||
|
||
// TODO PREDICTION
|
||
// When moving this to shared, fill in the user field
|
||
_unpoweredFlashlight.TryToggleLight(uid, user: null);
|
||
}
|
||
|
||
private void OnUiMessage(EntityUid uid, PdaComponent pda, PdaShowRingtoneMessage msg)
|
||
{
|
||
if (!PdaUiKey.Key.Equals(msg.UiKey))
|
||
return;
|
||
|
||
if (HasComp<RingerComponent>(uid))
|
||
_ringer.ToggleRingerUI(uid, msg.Actor);
|
||
}
|
||
|
||
private void OnUiMessage(EntityUid uid, PdaComponent pda, PdaShowMusicMessage msg)
|
||
{
|
||
if (!PdaUiKey.Key.Equals(msg.UiKey))
|
||
return;
|
||
|
||
if (TryComp<InstrumentComponent>(uid, out var instrument))
|
||
_instrument.ToggleInstrumentUi(uid, msg.Actor, instrument);
|
||
}
|
||
|
||
private void OnUiMessage(EntityUid uid, PdaComponent pda, PdaShowUplinkMessage msg)
|
||
{
|
||
if (!PdaUiKey.Key.Equals(msg.UiKey))
|
||
return;
|
||
|
||
// check if its locked again to prevent malicious clients opening locked uplinks
|
||
if (HasComp<UplinkComponent>(uid) && IsUnlocked(uid))
|
||
_store.ToggleUi(msg.Actor, uid);
|
||
}
|
||
|
||
private void OnUiMessage(EntityUid uid, PdaComponent pda, PdaLockUplinkMessage msg)
|
||
{
|
||
if (!PdaUiKey.Key.Equals(msg.UiKey))
|
||
return;
|
||
|
||
if (TryComp<RingerUplinkComponent>(uid, out var uplink))
|
||
{
|
||
_ringer.LockUplink(uid, uplink);
|
||
UpdatePdaUi(uid, pda);
|
||
}
|
||
}
|
||
|
||
private bool IsUnlocked(EntityUid uid)
|
||
{
|
||
return !TryComp<RingerUplinkComponent>(uid, out var uplink) || uplink.Unlocked;
|
||
}
|
||
|
||
private void UpdateStationName(EntityUid uid, PdaComponent pda)
|
||
{
|
||
var station = _station.GetOwningStation(uid);
|
||
pda.StationName = station is null ? null : Name(station.Value);
|
||
}
|
||
|
||
// Sunrise-start
|
||
private void UpdateEvacShuttle(EntityUid uid, PdaComponent pda)
|
||
{
|
||
TimeSpan? shuttleTime;
|
||
EvacShuttleStatus shuttleStatus;
|
||
if (_emergencyShuttleSystem.EmergencyShuttleArrived)
|
||
{
|
||
shuttleTime = _gameTiming.CurTime + TimeSpan.FromSeconds(_emergencyShuttleSystem.СonsoleAccumulator);
|
||
shuttleStatus = EvacShuttleStatus.WaitingToLaunch;
|
||
}
|
||
else
|
||
{
|
||
if (_roundEndSystem.ExpectedCountdownEnd != null)
|
||
{
|
||
shuttleTime = _roundEndSystem.ExpectedCountdownEnd;
|
||
shuttleStatus = EvacShuttleStatus.WaitingToArrival;
|
||
}
|
||
else
|
||
{
|
||
shuttleTime = _roundEndSystem.TimeToCallShuttle();
|
||
shuttleStatus = EvacShuttleStatus.WaitingToCall;
|
||
}
|
||
}
|
||
var station = _station.GetOwningStation(uid);
|
||
if (!TryComp<StationEmergencyShuttleComponent>(station, out var stationEmergencyShuttleComponent))
|
||
return;
|
||
|
||
pda.ShuttleStatus = shuttleStatus;
|
||
pda.ShuttleTime = shuttleTime;
|
||
}
|
||
// Sunrise-end
|
||
|
||
private void UpdateAlertLevel(EntityUid uid, PdaComponent pda)
|
||
{
|
||
var station = _station.GetOwningStation(uid);
|
||
if (!TryComp(station, out AlertLevelComponent? alertComp) ||
|
||
alertComp.AlertLevels == null)
|
||
return;
|
||
pda.StationAlertLevel = alertComp.CurrentLevel;
|
||
if (alertComp.AlertLevels.Levels.TryGetValue(alertComp.CurrentLevel, out var details))
|
||
pda.StationAlertColor = details.Color;
|
||
}
|
||
|
||
private string? GetDeviceNetAddress(EntityUid uid)
|
||
{
|
||
string? address = null;
|
||
|
||
if (TryComp(uid, out DeviceNetworkComponent? deviceNetworkComponent))
|
||
{
|
||
address = deviceNetworkComponent?.Address;
|
||
}
|
||
|
||
return address;
|
||
}
|
||
}
|
||
}
|