escape + kill objective changes (#17890)

* 3 space indent at start of line jumpscare

* _leftShuttles -> ShuttlesLeft

* stuff

* RequireDead override for the future

* fix 50% logic

* rouge

* pod 1984

* technically more "difficult"

---------

Co-authored-by: deltanedas <@deltanedas:kde.org>
This commit is contained in:
deltanedas
2023-07-09 22:00:08 +00:00
committed by GitHub
parent 53a0d1bc83
commit c346640ade
6 changed files with 441 additions and 394 deletions

View File

@@ -1,10 +1,8 @@
using Content.Server.Mind;
using Content.Server.Objectives.Interfaces;
using Content.Server.Shuttles.Components;
using Content.Server.Station.Components;
using Content.Server.Shuttles.Systems;
using Content.Shared.Cuffs.Components;
using JetBrains.Annotations;
using Robust.Shared.Map.Components;
using Robust.Shared.Utility;
namespace Content.Server.Objectives.Conditions
@@ -28,22 +26,6 @@ namespace Content.Server.Objectives.Conditions
public SpriteSpecifier Icon => new SpriteSpecifier.Rsi(new ("Structures/Furniture/chairs.rsi"), "shuttle");
private bool IsAgentOnShuttle(TransformComponent agentXform, EntityUid? shuttle)
{
if (shuttle == null)
return false;
var entMan = IoCManager.Resolve<IEntityManager>();
if (!entMan.TryGetComponent<MapGridComponent>(shuttle, out var shuttleGrid) ||
!entMan.TryGetComponent<TransformComponent>(shuttle, out var shuttleXform))
{
return false;
}
return shuttleXform.WorldMatrix.TransformBox(shuttleGrid.LocalAABB).Contains(agentXform.WorldPosition);
}
public float Progress
{
get {
@@ -54,25 +36,22 @@ namespace Content.Server.Objectives.Conditions
|| !entMan.TryGetComponent<TransformComponent>(_mind.OwnedEntity, out var xform))
return 0f;
var shuttleContainsAgent = false;
var agentIsAlive = !mindSystem.IsCharacterDeadIc(_mind);
var agentIsEscaping = true;
if (mindSystem.IsCharacterDeadIc(_mind))
return 0f;
if (entMan.TryGetComponent<CuffableComponent>(_mind.OwnedEntity, out var cuffed)
&& cuffed.CuffedHandCount > 0)
// You're not escaping if you're restrained!
agentIsEscaping = false;
// Any emergency shuttle counts for this objective.
foreach (var stationData in entMan.EntityQuery<StationEmergencyShuttleComponent>())
{
if (IsAgentOnShuttle(xform, stationData.EmergencyShuttle)) {
shuttleContainsAgent = true;
break;
}
// You're not escaping if you're restrained!
return 0f;
}
return (shuttleContainsAgent && agentIsAlive && agentIsEscaping) ? 1f : 0f;
// Any emergency shuttle counts for this objective, but not pods.
var emergencyShuttle = entMan.System<EmergencyShuttleSystem>();
if (!emergencyShuttle.IsTargetEscaping(_mind.OwnedEntity.Value))
return 0f;
return 1f;
}
}

View File

@@ -1,6 +1,9 @@
using Content.Server.Mind;
using Content.Server.Objectives.Interfaces;
using Content.Server.Shuttles.Systems;
using Content.Shared.CCVar;
using Content.Shared.Mobs.Systems;
using Robust.Shared.Configuration;
using Robust.Shared.Utility;
namespace Content.Server.Objectives.Conditions
@@ -12,6 +15,11 @@ namespace Content.Server.Objectives.Conditions
protected Mind.Mind? Target;
public abstract IObjectiveCondition GetAssigned(Mind.Mind mind);
/// <summary>
/// Whether the target must be truly dead, ignores missing evac.
/// </summary>
protected bool RequireDead = false;
public string Title
{
get
@@ -37,13 +45,37 @@ namespace Content.Server.Objectives.Conditions
{
get
{
var entityManager = IoCManager.Resolve<EntityManager>();
var mindSystem = entityManager.System<MindSystem>();
return Target == null || mindSystem.IsCharacterDeadIc(Target) ? 1f : 0f;
if (Target == null || Target.OwnedEntity == null)
return 1f;
var entMan = IoCManager.Resolve<EntityManager>();
var mindSystem = entMan.System<MindSystem>();
if (mindSystem.IsCharacterDeadIc(Target))
return 1f;
if (RequireDead)
return 0f;
// if evac is disabled then they really do have to be dead
var configMan = IoCManager.Resolve<IConfigurationManager>();
if (!configMan.GetCVar(CCVars.EmergencyShuttleEnabled))
return 0f;
// target is escaping so you fail
var emergencyShuttle = entMan.System<EmergencyShuttleSystem>();
if (emergencyShuttle.IsTargetEscaping(Target.OwnedEntity.Value))
return 0f;
// evac has left without the target, greentext since the target is afk in space with a full oxygen tank and coordinates off.
if (emergencyShuttle.ShuttlesLeft)
return 1f;
// if evac is still here and target hasn't boarded, show 50% to give you an indicator that you are doing good
return emergencyShuttle.EmergencyShuttleArrived ? 0f : 0.5f;
}
}
public float Difficulty => 2f;
public float Difficulty => 1.75f;
public bool Equals(IObjectiveCondition? other)
{

View File

@@ -71,7 +71,10 @@ public sealed partial class EmergencyShuttleSystem
/// </summary>
private bool _launchedShuttles;
private bool _leftShuttles;
/// <summary>
/// Have the emergency shuttles left for CentCom?
/// </summary>
public bool ShuttlesLeft;
/// <summary>
/// Have we announced the launch?
@@ -209,9 +212,9 @@ public sealed partial class EmergencyShuttleSystem
}
// Departed
if (!_leftShuttles && _consoleAccumulator <= 0f)
if (!ShuttlesLeft && _consoleAccumulator <= 0f)
{
_leftShuttles = true;
ShuttlesLeft = true;
_chatSystem.DispatchGlobalAnnouncement(Loc.GetString("emergency-shuttle-left", ("transitTime", $"{TransitTime:0}")));
Timer.Spawn((int) (TransitTime * 1000) + _bufferTime.Milliseconds, () => _roundEnd.EndRound(), _roundEndCancelToken?.Token ?? default);
@@ -309,7 +312,7 @@ public sealed partial class EmergencyShuttleSystem
{
_announced = false;
_roundEndCancelToken = null;
_leftShuttles = false;
ShuttlesLeft = false;
_launchedShuttles = false;
_consoleAccumulator = float.MinValue;
EarlyLaunchAuthorized = false;

View File

@@ -31,355 +31,388 @@ namespace Content.Server.Shuttles.Systems;
public sealed partial class EmergencyShuttleSystem : EntitySystem
{
/*
* Handles the escape shuttle + CentCom.
*/
[Dependency] private readonly IAdminLogManager _logger = default!;
[Dependency] private readonly IAdminManager _admin = default!;
[Dependency] private readonly IConfigurationManager _configManager = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly AccessReaderSystem _reader = default!;
[Dependency] private readonly ChatSystem _chatSystem = default!;
[Dependency] private readonly CommunicationsConsoleSystem _commsConsole = default!;
[Dependency] private readonly DockingSystem _dock = default!;
[Dependency] private readonly IdCardSystem _idSystem = default!;
[Dependency] private readonly MapLoaderSystem _map = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly RoundEndSystem _roundEnd = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly ShuttleSystem _shuttle = default!;
[Dependency] private readonly StationSystem _station = default!;
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
private ISawmill _sawmill = default!;
private const float ShuttleSpawnBuffer = 1f;
private bool _emergencyShuttleEnabled;
private const string DockTag = "DockEmergency";
public override void Initialize()
{
_sawmill = Logger.GetSawmill("shuttle.emergency");
_emergencyShuttleEnabled = _configManager.GetCVar(CCVars.EmergencyShuttleEnabled);
// Don't immediately invoke as roundstart will just handle it.
_configManager.OnValueChanged(CCVars.EmergencyShuttleEnabled, SetEmergencyShuttleEnabled);
SubscribeLocalEvent<RoundStartingEvent>(OnRoundStart);
SubscribeLocalEvent<StationEmergencyShuttleComponent, ComponentStartup>(OnStationStartup);
SubscribeLocalEvent<StationCentcommComponent, ComponentShutdown>(OnCentcommShutdown);
SubscribeLocalEvent<StationCentcommComponent, ComponentInit>(OnCentcommInit);
SubscribeNetworkEvent<EmergencyShuttleRequestPositionMessage>(OnShuttleRequestPosition);
InitializeEmergencyConsole();
}
private void OnRoundStart(RoundStartingEvent ev)
{
CleanupEmergencyConsole();
_roundEndCancelToken?.Cancel();
_roundEndCancelToken = new CancellationTokenSource();
}
private void OnCentcommShutdown(EntityUid uid, StationCentcommComponent component, ComponentShutdown args)
{
QueueDel(component.Entity);
component.Entity = EntityUid.Invalid;
if (_mapManager.MapExists(component.MapId))
_mapManager.DeleteMap(component.MapId);
component.MapId = MapId.Nullspace;
}
private void SetEmergencyShuttleEnabled(bool value)
{
if (_emergencyShuttleEnabled == value)
return;
_emergencyShuttleEnabled = value;
if (value)
{
SetupEmergencyShuttle();
}
else
{
CleanupEmergencyShuttle();
}
}
private void CleanupEmergencyShuttle()
{
var query = AllEntityQuery<StationCentcommComponent>();
while (query.MoveNext(out var uid, out _))
{
RemCompDeferred<StationCentcommComponent>(uid);
}
}
public override void Update(float frameTime)
{
base.Update(frameTime);
UpdateEmergencyConsole(frameTime);
}
public override void Shutdown()
{
_configManager.UnsubValueChanged(CCVars.EmergencyShuttleEnabled, SetEmergencyShuttleEnabled);
ShutdownEmergencyConsole();
}
/// <summary>
/// If the client is requesting debug info on where an emergency shuttle would dock.
/// </summary>
private void OnShuttleRequestPosition(EmergencyShuttleRequestPositionMessage msg, EntitySessionEventArgs args)
{
if (!_admin.IsAdmin((IPlayerSession) args.SenderSession))
return;
var player = args.SenderSession.AttachedEntity;
if (player is null)
return;
var station = _station.GetOwningStation(player.Value);
if (!TryComp<StationEmergencyShuttleComponent>(station, out var stationShuttle) ||
!HasComp<ShuttleComponent>(stationShuttle.EmergencyShuttle))
{
return;
}
var targetGrid = _station.GetLargestGrid(Comp<StationDataComponent>(station.Value));
if (targetGrid == null)
return;
var config = _dock.GetDockingConfig(stationShuttle.EmergencyShuttle.Value, targetGrid.Value, DockTag);
if (config == null)
return;
RaiseNetworkEvent(new EmergencyShuttlePositionMessage()
{
StationUid = targetGrid,
Position = config.Area,
});
}
/// <summary>
/// Calls the emergency shuttle for the station.
/// </summary>
public void CallEmergencyShuttle(EntityUid stationUid, StationEmergencyShuttleComponent? stationShuttle = null)
{
if (!Resolve(stationUid, ref stationShuttle) ||
!TryComp<TransformComponent>(stationShuttle.EmergencyShuttle, out var xform) ||
!TryComp<ShuttleComponent>(stationShuttle.EmergencyShuttle, out var shuttle))
{
return;
}
var targetGrid = _station.GetLargestGrid(Comp<StationDataComponent>(stationUid));
// UHH GOOD LUCK
if (targetGrid == null)
{
_logger.Add(LogType.EmergencyShuttle, LogImpact.High, $"Emergency shuttle {ToPrettyString(stationUid)} unable to dock with station {ToPrettyString(stationUid)}");
_chatSystem.DispatchStationAnnouncement(stationUid, Loc.GetString("emergency-shuttle-good-luck"), playDefaultSound: false);
// TODO: Need filter extensions or something don't blame me.
_audio.PlayGlobal("/Audio/Misc/notice1.ogg", Filter.Broadcast(), true);
return;
}
var xformQuery = GetEntityQuery<TransformComponent>();
if (_shuttle.TryFTLDock(stationShuttle.EmergencyShuttle.Value, shuttle, targetGrid.Value, DockTag))
{
if (TryComp<TransformComponent>(targetGrid.Value, out var targetXform))
{
var angle = _dock.GetAngle(stationShuttle.EmergencyShuttle.Value, xform, targetGrid.Value, targetXform, xformQuery);
_chatSystem.DispatchStationAnnouncement(stationUid, Loc.GetString("emergency-shuttle-docked", ("time", $"{_consoleAccumulator:0}"), ("direction", angle.GetDir())), playDefaultSound: false);
}
_logger.Add(LogType.EmergencyShuttle, LogImpact.High, $"Emergency shuttle {ToPrettyString(stationUid)} docked with stations");
// TODO: Need filter extensions or something don't blame me.
_audio.PlayGlobal("/Audio/Announcements/shuttle_dock.ogg", Filter.Broadcast(), true);
}
else
{
if (TryComp<TransformComponent>(targetGrid.Value, out var targetXform))
{
var angle = _dock.GetAngle(stationShuttle.EmergencyShuttle.Value, xform, targetGrid.Value, targetXform, xformQuery);
_chatSystem.DispatchStationAnnouncement(stationUid, Loc.GetString("emergency-shuttle-nearby", ("direction", angle.GetDir())), playDefaultSound: false);
}
_logger.Add(LogType.EmergencyShuttle, LogImpact.High, $"Emergency shuttle {ToPrettyString(stationUid)} unable to find a valid docking port for {ToPrettyString(stationUid)}");
// TODO: Need filter extensions or something don't blame me.
_audio.PlayGlobal("/Audio/Misc/notice1.ogg", Filter.Broadcast(), true);
}
}
private void OnCentcommInit(EntityUid uid, StationCentcommComponent component, ComponentInit args)
{
if (!_emergencyShuttleEnabled)
return;
// Post mapinit? fancy
if (TryComp<TransformComponent>(component.Entity, out var xform))
{
component.MapId = xform.MapID;
return;
}
AddCentcomm(component);
}
private void OnStationStartup(EntityUid uid, StationEmergencyShuttleComponent component, ComponentStartup args)
{
AddEmergencyShuttle(uid, component);
}
/// <summary>
/// Spawns the emergency shuttle for each station and starts the countdown until controls unlock.
/// </summary>
public void CallEmergencyShuttle()
{
if (EmergencyShuttleArrived)
return;
if (!_emergencyShuttleEnabled)
{
_roundEnd.EndRound();
return;
}
_consoleAccumulator = _configManager.GetCVar(CCVars.EmergencyShuttleDockTime);
EmergencyShuttleArrived = true;
var query = AllEntityQuery<StationEmergencyShuttleComponent>();
while (query.MoveNext(out var uid, out var comp))
{
CallEmergencyShuttle(uid, comp);
}
_commsConsole.UpdateCommsConsoleInterface();
}
private void SetupEmergencyShuttle()
{
if (!_emergencyShuttleEnabled)
return;
var centcommQuery = AllEntityQuery<StationCentcommComponent>();
while (centcommQuery.MoveNext(out var centcomm))
{
AddCentcomm(centcomm);
}
var query = AllEntityQuery<StationEmergencyShuttleComponent>();
while (query.MoveNext(out var uid, out var comp))
{
AddEmergencyShuttle(uid, comp);
}
}
private void AddCentcomm(StationCentcommComponent component)
{
// Check for existing centcomms and just point to that
var query = AllEntityQuery<StationCentcommComponent>();
while (query.MoveNext(out var otherComp))
{
if (otherComp == component)
continue;
component.MapId = otherComp.MapId;
component.ShuttleIndex = otherComp.ShuttleIndex;
return;
}
var mapId = _mapManager.CreateMap();
component.MapId = mapId;
if (!string.IsNullOrEmpty(component.Map.ToString()))
{
var ent = _map.LoadGrid(mapId, component.Map.ToString());
if (ent != null)
{
component.Entity = ent.Value;
_shuttle.AddFTLDestination(ent.Value, false);
}
}
else
{
_sawmill.Warning("No CentComm map found, skipping setup.");
}
}
public HashSet<MapId> GetCentcommMaps()
{
var query = AllEntityQuery<StationCentcommComponent>();
var maps = new HashSet<MapId>(Count<StationCentcommComponent>());
while (query.MoveNext(out var comp))
{
maps.Add(comp.MapId);
}
return maps;
}
private void AddEmergencyShuttle(EntityUid uid, StationEmergencyShuttleComponent component)
{
if (!_emergencyShuttleEnabled
|| component.EmergencyShuttle != null ||
!TryComp<StationCentcommComponent>(uid, out var centcomm))
{
return;
}
// Load escape shuttle
var shuttlePath = component.EmergencyShuttlePath;
var shuttle = _map.LoadGrid(centcomm.MapId, shuttlePath.ToString(), new MapLoadOptions()
{
// Should be far enough... right? I'm too lazy to bounds check CentCom rn.
Offset = new Vector2(500f + centcomm.ShuttleIndex, 0f)
});
if (shuttle == null)
{
_sawmill.Error($"Unable to spawn emergency shuttle {shuttlePath} for {ToPrettyString(uid)}");
return;
}
centcomm.ShuttleIndex += _mapManager.GetGrid(shuttle.Value).LocalAABB.Width + ShuttleSpawnBuffer;
// Update indices for all centcomm comps pointing to same map
var query = AllEntityQuery<StationCentcommComponent>();
while (query.MoveNext(out var comp))
{
if (comp == centcomm || comp.MapId != centcomm.MapId)
continue;
comp.ShuttleIndex = centcomm.ShuttleIndex;
}
component.EmergencyShuttle = shuttle;
EnsureComp<ProtectedGridComponent>(shuttle.Value);
EnsureComp<PreventPilotComponent>(shuttle.Value);
}
private void OnEscapeUnpaused(EntityUid uid, EscapePodComponent component, ref EntityUnpausedEvent args)
{
if (component.LaunchTime == null)
return;
component.LaunchTime = component.LaunchTime.Value + args.PausedTime;
}
/*
* Handles the escape shuttle + CentCom.
*/
[Dependency] private readonly IAdminLogManager _logger = default!;
[Dependency] private readonly IAdminManager _admin = default!;
[Dependency] private readonly IConfigurationManager _configManager = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly AccessReaderSystem _reader = default!;
[Dependency] private readonly ChatSystem _chatSystem = default!;
[Dependency] private readonly CommunicationsConsoleSystem _commsConsole = default!;
[Dependency] private readonly DockingSystem _dock = default!;
[Dependency] private readonly IdCardSystem _idSystem = default!;
[Dependency] private readonly MapLoaderSystem _map = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly RoundEndSystem _roundEnd = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly ShuttleSystem _shuttle = default!;
[Dependency] private readonly StationSystem _station = default!;
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
private ISawmill _sawmill = default!;
private const float ShuttleSpawnBuffer = 1f;
private bool _emergencyShuttleEnabled;
private const string DockTag = "DockEmergency";
public override void Initialize()
{
_sawmill = Logger.GetSawmill("shuttle.emergency");
_emergencyShuttleEnabled = _configManager.GetCVar(CCVars.EmergencyShuttleEnabled);
// Don't immediately invoke as roundstart will just handle it.
_configManager.OnValueChanged(CCVars.EmergencyShuttleEnabled, SetEmergencyShuttleEnabled);
SubscribeLocalEvent<RoundStartingEvent>(OnRoundStart);
SubscribeLocalEvent<StationEmergencyShuttleComponent, ComponentStartup>(OnStationStartup);
SubscribeLocalEvent<StationCentcommComponent, ComponentShutdown>(OnCentcommShutdown);
SubscribeLocalEvent<StationCentcommComponent, ComponentInit>(OnCentcommInit);
SubscribeNetworkEvent<EmergencyShuttleRequestPositionMessage>(OnShuttleRequestPosition);
InitializeEmergencyConsole();
}
private void OnRoundStart(RoundStartingEvent ev)
{
CleanupEmergencyConsole();
_roundEndCancelToken?.Cancel();
_roundEndCancelToken = new CancellationTokenSource();
}
private void OnCentcommShutdown(EntityUid uid, StationCentcommComponent component, ComponentShutdown args)
{
QueueDel(component.Entity);
component.Entity = EntityUid.Invalid;
if (_mapManager.MapExists(component.MapId))
_mapManager.DeleteMap(component.MapId);
component.MapId = MapId.Nullspace;
}
private void SetEmergencyShuttleEnabled(bool value)
{
if (_emergencyShuttleEnabled == value)
return;
_emergencyShuttleEnabled = value;
if (value)
{
SetupEmergencyShuttle();
}
else
{
CleanupEmergencyShuttle();
}
}
private void CleanupEmergencyShuttle()
{
var query = AllEntityQuery<StationCentcommComponent>();
while (query.MoveNext(out var uid, out _))
{
RemCompDeferred<StationCentcommComponent>(uid);
}
}
public override void Update(float frameTime)
{
base.Update(frameTime);
UpdateEmergencyConsole(frameTime);
}
public override void Shutdown()
{
_configManager.UnsubValueChanged(CCVars.EmergencyShuttleEnabled, SetEmergencyShuttleEnabled);
ShutdownEmergencyConsole();
}
/// <summary>
/// If the client is requesting debug info on where an emergency shuttle would dock.
/// </summary>
private void OnShuttleRequestPosition(EmergencyShuttleRequestPositionMessage msg, EntitySessionEventArgs args)
{
if (!_admin.IsAdmin((IPlayerSession) args.SenderSession))
return;
var player = args.SenderSession.AttachedEntity;
if (player is null)
return;
var station = _station.GetOwningStation(player.Value);
if (!TryComp<StationEmergencyShuttleComponent>(station, out var stationShuttle) ||
!HasComp<ShuttleComponent>(stationShuttle.EmergencyShuttle))
{
return;
}
var targetGrid = _station.GetLargestGrid(Comp<StationDataComponent>(station.Value));
if (targetGrid == null)
return;
var config = _dock.GetDockingConfig(stationShuttle.EmergencyShuttle.Value, targetGrid.Value, DockTag);
if (config == null)
return;
RaiseNetworkEvent(new EmergencyShuttlePositionMessage()
{
StationUid = targetGrid,
Position = config.Area,
});
}
/// <summary>
/// Calls the emergency shuttle for the station.
/// </summary>
public void CallEmergencyShuttle(EntityUid stationUid, StationEmergencyShuttleComponent? stationShuttle = null)
{
if (!Resolve(stationUid, ref stationShuttle) ||
!TryComp<TransformComponent>(stationShuttle.EmergencyShuttle, out var xform) ||
!TryComp<ShuttleComponent>(stationShuttle.EmergencyShuttle, out var shuttle))
{
return;
}
var targetGrid = _station.GetLargestGrid(Comp<StationDataComponent>(stationUid));
// UHH GOOD LUCK
if (targetGrid == null)
{
_logger.Add(LogType.EmergencyShuttle, LogImpact.High, $"Emergency shuttle {ToPrettyString(stationUid)} unable to dock with station {ToPrettyString(stationUid)}");
_chatSystem.DispatchStationAnnouncement(stationUid, Loc.GetString("emergency-shuttle-good-luck"), playDefaultSound: false);
// TODO: Need filter extensions or something don't blame me.
_audio.PlayGlobal("/Audio/Misc/notice1.ogg", Filter.Broadcast(), true);
return;
}
var xformQuery = GetEntityQuery<TransformComponent>();
if (_shuttle.TryFTLDock(stationShuttle.EmergencyShuttle.Value, shuttle, targetGrid.Value, DockTag))
{
if (TryComp<TransformComponent>(targetGrid.Value, out var targetXform))
{
var angle = _dock.GetAngle(stationShuttle.EmergencyShuttle.Value, xform, targetGrid.Value, targetXform, xformQuery);
_chatSystem.DispatchStationAnnouncement(stationUid, Loc.GetString("emergency-shuttle-docked", ("time", $"{_consoleAccumulator:0}"), ("direction", angle.GetDir())), playDefaultSound: false);
}
_logger.Add(LogType.EmergencyShuttle, LogImpact.High, $"Emergency shuttle {ToPrettyString(stationUid)} docked with stations");
// TODO: Need filter extensions or something don't blame me.
_audio.PlayGlobal("/Audio/Announcements/shuttle_dock.ogg", Filter.Broadcast(), true);
}
else
{
if (TryComp<TransformComponent>(targetGrid.Value, out var targetXform))
{
var angle = _dock.GetAngle(stationShuttle.EmergencyShuttle.Value, xform, targetGrid.Value, targetXform, xformQuery);
_chatSystem.DispatchStationAnnouncement(stationUid, Loc.GetString("emergency-shuttle-nearby", ("direction", angle.GetDir())), playDefaultSound: false);
}
_logger.Add(LogType.EmergencyShuttle, LogImpact.High, $"Emergency shuttle {ToPrettyString(stationUid)} unable to find a valid docking port for {ToPrettyString(stationUid)}");
// TODO: Need filter extensions or something don't blame me.
_audio.PlayGlobal("/Audio/Misc/notice1.ogg", Filter.Broadcast(), true);
}
}
private void OnCentcommInit(EntityUid uid, StationCentcommComponent component, ComponentInit args)
{
if (!_emergencyShuttleEnabled)
return;
// Post mapinit? fancy
if (TryComp<TransformComponent>(component.Entity, out var xform))
{
component.MapId = xform.MapID;
return;
}
AddCentcomm(component);
}
private void OnStationStartup(EntityUid uid, StationEmergencyShuttleComponent component, ComponentStartup args)
{
AddEmergencyShuttle(uid, component);
}
/// <summary>
/// Spawns the emergency shuttle for each station and starts the countdown until controls unlock.
/// </summary>
public void CallEmergencyShuttle()
{
if (EmergencyShuttleArrived)
return;
if (!_emergencyShuttleEnabled)
{
_roundEnd.EndRound();
return;
}
_consoleAccumulator = _configManager.GetCVar(CCVars.EmergencyShuttleDockTime);
EmergencyShuttleArrived = true;
var query = AllEntityQuery<StationEmergencyShuttleComponent>();
while (query.MoveNext(out var uid, out var comp))
{
CallEmergencyShuttle(uid, comp);
}
_commsConsole.UpdateCommsConsoleInterface();
}
private void SetupEmergencyShuttle()
{
if (!_emergencyShuttleEnabled)
return;
var centcommQuery = AllEntityQuery<StationCentcommComponent>();
while (centcommQuery.MoveNext(out var centcomm))
{
AddCentcomm(centcomm);
}
var query = AllEntityQuery<StationEmergencyShuttleComponent>();
while (query.MoveNext(out var uid, out var comp))
{
AddEmergencyShuttle(uid, comp);
}
}
private void AddCentcomm(StationCentcommComponent component)
{
// Check for existing centcomms and just point to that
var query = AllEntityQuery<StationCentcommComponent>();
while (query.MoveNext(out var otherComp))
{
if (otherComp == component)
continue;
component.MapId = otherComp.MapId;
component.ShuttleIndex = otherComp.ShuttleIndex;
return;
}
var mapId = _mapManager.CreateMap();
component.MapId = mapId;
if (!string.IsNullOrEmpty(component.Map.ToString()))
{
var ent = _map.LoadGrid(mapId, component.Map.ToString());
if (ent != null)
{
component.Entity = ent.Value;
_shuttle.AddFTLDestination(ent.Value, false);
}
}
else
{
_sawmill.Warning("No CentComm map found, skipping setup.");
}
}
public HashSet<MapId> GetCentcommMaps()
{
var query = AllEntityQuery<StationCentcommComponent>();
var maps = new HashSet<MapId>(Count<StationCentcommComponent>());
while (query.MoveNext(out var comp))
{
maps.Add(comp.MapId);
}
return maps;
}
private void AddEmergencyShuttle(EntityUid uid, StationEmergencyShuttleComponent component)
{
if (!_emergencyShuttleEnabled
|| component.EmergencyShuttle != null ||
!TryComp<StationCentcommComponent>(uid, out var centcomm))
{
return;
}
// Load escape shuttle
var shuttlePath = component.EmergencyShuttlePath;
var shuttle = _map.LoadGrid(centcomm.MapId, shuttlePath.ToString(), new MapLoadOptions()
{
// Should be far enough... right? I'm too lazy to bounds check CentCom rn.
Offset = new Vector2(500f + centcomm.ShuttleIndex, 0f)
});
if (shuttle == null)
{
_sawmill.Error($"Unable to spawn emergency shuttle {shuttlePath} for {ToPrettyString(uid)}");
return;
}
centcomm.ShuttleIndex += _mapManager.GetGrid(shuttle.Value).LocalAABB.Width + ShuttleSpawnBuffer;
// Update indices for all centcomm comps pointing to same map
var query = AllEntityQuery<StationCentcommComponent>();
while (query.MoveNext(out var comp))
{
if (comp == centcomm || comp.MapId != centcomm.MapId)
continue;
comp.ShuttleIndex = centcomm.ShuttleIndex;
}
component.EmergencyShuttle = shuttle;
EnsureComp<ProtectedGridComponent>(shuttle.Value);
EnsureComp<PreventPilotComponent>(shuttle.Value);
}
private void OnEscapeUnpaused(EntityUid uid, EscapePodComponent component, ref EntityUnpausedEvent args)
{
if (component.LaunchTime == null)
return;
component.LaunchTime = component.LaunchTime.Value + args.PausedTime;
}
/// <summary>
/// Returns whether a target is escaping on the emergency shuttle, but only if evac has arrived.
/// </summary>
public bool IsTargetEscaping(EntityUid target)
{
// if evac isn't here then sitting in a pod doesn't return true
if (!EmergencyShuttleArrived)
return false;
// check each emergency shuttle
var xform = Transform(target);
foreach (var stationData in EntityQuery<StationEmergencyShuttleComponent>())
{
if (stationData.EmergencyShuttle == null)
continue;
if (IsOnGrid(xform, stationData.EmergencyShuttle.Value))
{
return true;
}
}
return false;
}
private bool IsOnGrid(TransformComponent xform, EntityUid shuttle, MapGridComponent? grid = null, TransformComponent? shuttleXform = null)
{
if (!Resolve(shuttle, ref grid, ref shuttleXform))
return false;
return shuttleXform.WorldMatrix.TransformBox(grid.LocalAABB).Contains(xform.WorldPosition);
}
}

View File

@@ -1,2 +1,2 @@
objective-condition-escape-shuttle-title = Escape on the emergency shuttle alive and unrestrained.
objective-condition-escape-shuttle-title = Escape to centcom alive and unrestrained.
objective-condition-escape-shuttle-description = One of our undercover agents will debrief you when you arrive. Don't show up in cuffs.

View File

@@ -1,2 +1,2 @@
objective-condition-kill-person-title = Kill {$targetName}, {CAPITALIZE($job)}
objective-condition-kill-person-description = Do it however you like, just make sure they don't last the shift.
objective-condition-kill-person-title = Kill or maroon {$targetName}, {CAPITALIZE($job)}
objective-condition-kill-person-description = Do it however you like, just make sure they don't make it to centcom.