Add the ability to spawn in as a vent critter at will (#6187)
* Add the ability to spawn in as a vent critter at will * cooldown
This commit is contained in:
parent
6bd4603734
commit
acc9361a34
|
|
@ -2,10 +2,12 @@
|
||||||
using Content.Client.Ghost;
|
using Content.Client.Ghost;
|
||||||
using Content.Client.UserInterface.Systems.Gameplay;
|
using Content.Client.UserInterface.Systems.Gameplay;
|
||||||
using Content.Client.UserInterface.Systems.Ghost.Widgets;
|
using Content.Client.UserInterface.Systems.Ghost.Widgets;
|
||||||
|
using Content.Shared._DV.Ghost.Roles; // DeltaV - freeform ghosties
|
||||||
using Content.Shared.Ghost;
|
using Content.Shared.Ghost;
|
||||||
using Robust.Shared.Console; // Frontier
|
using Robust.Shared.Console; // Frontier
|
||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Client.UserInterface.Controllers;
|
using Robust.Client.UserInterface.Controllers;
|
||||||
|
using Robust.Shared.Prototypes; // DeltaV - freeform ghosties
|
||||||
using Content.Shared._Corvax.Respawn; // Frontier
|
using Content.Shared._Corvax.Respawn; // Frontier
|
||||||
|
|
||||||
namespace Content.Client.UserInterface.Systems.Ghost;
|
namespace Content.Client.UserInterface.Systems.Ghost;
|
||||||
|
|
@ -33,6 +35,7 @@ public sealed class GhostUIController : UIController, IOnSystemChanged<GhostSyst
|
||||||
|
|
||||||
// DeltaV
|
// DeltaV
|
||||||
SubscribeNetworkEvent<RespawnResetEvent>(OnRespawnReseted);
|
SubscribeNetworkEvent<RespawnResetEvent>(OnRespawnReseted);
|
||||||
|
SubscribeNetworkEvent<DVSpawnableGhostRoleCooldownUpdateEvent>(OnVentCritterCooldownUpdate); // DeltaV - freeform ghosties
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnScreenLoad()
|
private void OnScreenLoad()
|
||||||
|
|
@ -151,6 +154,8 @@ public sealed class GhostUIController : UIController, IOnSystemChanged<GhostSyst
|
||||||
Gui.RequestWarpsPressed += RequestWarps;
|
Gui.RequestWarpsPressed += RequestWarps;
|
||||||
Gui.ReturnToBodyPressed += ReturnToBody;
|
Gui.ReturnToBodyPressed += ReturnToBody;
|
||||||
Gui.GhostRolesPressed += GhostRolesPressed;
|
Gui.GhostRolesPressed += GhostRolesPressed;
|
||||||
|
Gui.VentCritterPressed += OnVentCritterPressed; // DeltaV - freeform ghosties
|
||||||
|
Gui.VentCritterSelected += OnVentCritterSelected; // DeltaV - freeform ghosties
|
||||||
Gui.TargetWindow.WarpClicked += OnWarpClicked;
|
Gui.TargetWindow.WarpClicked += OnWarpClicked;
|
||||||
Gui.TargetWindow.OnGhostnadoClicked += OnGhostnadoClicked;
|
Gui.TargetWindow.OnGhostnadoClicked += OnGhostnadoClicked;
|
||||||
Gui.GhostRespawnPressed += GuiOnGhostRespawnPressed; // Frontier
|
Gui.GhostRespawnPressed += GuiOnGhostRespawnPressed; // Frontier
|
||||||
|
|
@ -173,6 +178,8 @@ public sealed class GhostUIController : UIController, IOnSystemChanged<GhostSyst
|
||||||
Gui.RequestWarpsPressed -= RequestWarps;
|
Gui.RequestWarpsPressed -= RequestWarps;
|
||||||
Gui.ReturnToBodyPressed -= ReturnToBody;
|
Gui.ReturnToBodyPressed -= ReturnToBody;
|
||||||
Gui.GhostRolesPressed -= GhostRolesPressed;
|
Gui.GhostRolesPressed -= GhostRolesPressed;
|
||||||
|
Gui.VentCritterPressed -= OnVentCritterPressed; // DeltaV - freeform ghosties
|
||||||
|
Gui.VentCritterSelected -= OnVentCritterSelected; // DeltaV - freeform ghosties
|
||||||
Gui.TargetWindow.WarpClicked -= OnWarpClicked;
|
Gui.TargetWindow.WarpClicked -= OnWarpClicked;
|
||||||
Gui.GhostRespawnPressed -= GuiOnGhostRespawnPressed; // Frontier
|
Gui.GhostRespawnPressed -= GuiOnGhostRespawnPressed; // Frontier
|
||||||
|
|
||||||
|
|
@ -195,4 +202,21 @@ public sealed class GhostUIController : UIController, IOnSystemChanged<GhostSyst
|
||||||
{
|
{
|
||||||
_system?.OpenGhostRoles();
|
_system?.OpenGhostRoles();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Begin DeltaV - freeform ghosties
|
||||||
|
private void OnVentCritterPressed()
|
||||||
|
{
|
||||||
|
_net.SendSystemNetworkMessage(new DVSpawnableGhostRoleCooldownRequestEvent());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnVentCritterSelected(ProtoId<DVSpawnableGhostRolePrototype> role)
|
||||||
|
{
|
||||||
|
_net.SendSystemNetworkMessage(new DVSpawnableGhostRoleRequestEvent(role));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnVentCritterCooldownUpdate(DVSpawnableGhostRoleCooldownUpdateEvent msg, EntitySessionEventArgs args)
|
||||||
|
{
|
||||||
|
Gui?.VentCritterWindow.SetCooldownEnd(msg.CooldownEnd);
|
||||||
|
}
|
||||||
|
// End DeltaV - freeform ghosties
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
<Button Name="ReturnToBodyButton" Text="{Loc ghost-gui-return-to-body-button}" />
|
<Button Name="ReturnToBodyButton" Text="{Loc ghost-gui-return-to-body-button}" />
|
||||||
<Button Name="GhostWarpButton" Text="{Loc ghost-gui-ghost-warp-button}" />
|
<Button Name="GhostWarpButton" Text="{Loc ghost-gui-ghost-warp-button}" />
|
||||||
<Button Name="GhostRolesButton" />
|
<Button Name="GhostRolesButton" />
|
||||||
|
<Button Name="VentCritterButton" Text="{Loc ghost-gui-spawn-vent-critter-button}" /> <!-- DeltaV: freeform ghosties -->
|
||||||
<Button Name="GhostRespawnButton" Text="{Loc ghost-gui-respawn}" /> <!-- Frontier -->
|
<Button Name="GhostRespawnButton" Text="{Loc ghost-gui-respawn}" /> <!-- Frontier -->
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</widgets:GhostGui>
|
</widgets:GhostGui>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
|
using Content.Client._DV.Ghost; // DeltaV - freeform ghosties
|
||||||
using Content.Client.Stylesheets;
|
using Content.Client.Stylesheets;
|
||||||
using Content.Client.UserInterface.Systems.Ghost.Controls;
|
using Content.Client.UserInterface.Systems.Ghost.Controls;
|
||||||
|
using Content.Shared._DV.Ghost.Roles; // DeltaV - freeform ghosties
|
||||||
using Robust.Client.AutoGenerated;
|
using Robust.Client.AutoGenerated;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Client.UserInterface.XAML;
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
using Robust.Shared.Prototypes; // DeltaV - freeform ghosties
|
||||||
using Robust.Shared.Timing; // Frontier
|
using Robust.Shared.Timing; // Frontier
|
||||||
using Content.Client._NF.UserInterface.Systems.Ghost.Controls; // Frontier
|
using Content.Client._NF.UserInterface.Systems.Ghost.Controls; // Frontier
|
||||||
|
|
||||||
|
|
@ -17,10 +20,13 @@ public sealed partial class GhostGui : UIWidget
|
||||||
|
|
||||||
public GhostTargetWindow TargetWindow { get; }
|
public GhostTargetWindow TargetWindow { get; }
|
||||||
public GhostRespawnRulesWindow RulesWindow { get; } // Frontier
|
public GhostRespawnRulesWindow RulesWindow { get; } // Frontier
|
||||||
|
public DVSpawnableGhostRoleWindow VentCritterWindow { get; } // DeltaV - freeform ghosties
|
||||||
|
|
||||||
public event Action? RequestWarpsPressed;
|
public event Action? RequestWarpsPressed;
|
||||||
public event Action? ReturnToBodyPressed;
|
public event Action? ReturnToBodyPressed;
|
||||||
public event Action? GhostRolesPressed;
|
public event Action? GhostRolesPressed;
|
||||||
|
public event Action? VentCritterPressed; // DeltaV - freeform ghosties
|
||||||
|
public event Action<ProtoId<DVSpawnableGhostRolePrototype>>? VentCritterSelected; // DeltaV - freeform ghosties
|
||||||
public event Action? GhostRespawnPressed; // Frontier
|
public event Action? GhostRespawnPressed; // Frontier
|
||||||
private int _prevNumberRoles;
|
private int _prevNumberRoles;
|
||||||
|
|
||||||
|
|
@ -31,6 +37,8 @@ public sealed partial class GhostGui : UIWidget
|
||||||
TargetWindow = new GhostTargetWindow();
|
TargetWindow = new GhostTargetWindow();
|
||||||
RulesWindow = new GhostRespawnRulesWindow(); // Frontier
|
RulesWindow = new GhostRespawnRulesWindow(); // Frontier
|
||||||
RulesWindow.RespawnButton.OnPressed += _ => GhostRespawnPressed?.Invoke(); // Frontier
|
RulesWindow.RespawnButton.OnPressed += _ => GhostRespawnPressed?.Invoke(); // Frontier
|
||||||
|
VentCritterWindow = new DVSpawnableGhostRoleWindow(); // DeltaV - freeform ghosties
|
||||||
|
VentCritterWindow.RoleSelected += role => VentCritterSelected?.Invoke(role); // DeltaV - freeform ghosties
|
||||||
|
|
||||||
MouseFilter = MouseFilterMode.Ignore;
|
MouseFilter = MouseFilterMode.Ignore;
|
||||||
|
|
||||||
|
|
@ -38,12 +46,18 @@ public sealed partial class GhostGui : UIWidget
|
||||||
ReturnToBodyButton.OnPressed += _ => ReturnToBodyPressed?.Invoke();
|
ReturnToBodyButton.OnPressed += _ => ReturnToBodyPressed?.Invoke();
|
||||||
GhostRolesButton.OnPressed += _ => GhostRolesPressed?.Invoke();
|
GhostRolesButton.OnPressed += _ => GhostRolesPressed?.Invoke();
|
||||||
GhostRolesButton.OnPressed += _ => GhostRolesButton.StyleClasses.Remove(StyleClass.Negative);
|
GhostRolesButton.OnPressed += _ => GhostRolesButton.StyleClasses.Remove(StyleClass.Negative);
|
||||||
|
VentCritterButton.OnPressed += _ =>
|
||||||
|
{
|
||||||
|
VentCritterPressed?.Invoke();
|
||||||
|
VentCritterWindow.OpenCentered();
|
||||||
|
}; // DeltaV - freeform ghosties
|
||||||
GhostRespawnButton.OnPressed += _ => RulesWindow.OpenCentered(); // Frontier
|
GhostRespawnButton.OnPressed += _ => RulesWindow.OpenCentered(); // Frontier
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Hide()
|
public void Hide()
|
||||||
{
|
{
|
||||||
TargetWindow.Close();
|
TargetWindow.Close();
|
||||||
|
VentCritterWindow.Close(); // DeltaV - freeform ghosties
|
||||||
Visible = false;
|
Visible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -97,6 +111,7 @@ public sealed partial class GhostGui : UIWidget
|
||||||
if (disposing)
|
if (disposing)
|
||||||
{
|
{
|
||||||
TargetWindow.Dispose();
|
TargetWindow.Dispose();
|
||||||
|
VentCritterWindow.Dispose(); // DeltaV - freeform ghosties
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,145 @@
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
using Content.Client.UserInterface.Controls;
|
||||||
|
using Content.Client.UserInterface.Systems.Ghost.Controls.Roles;
|
||||||
|
using Content.Shared._DV.Ghost.Roles;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
|
namespace Content.Client._DV.Ghost;
|
||||||
|
|
||||||
|
public sealed class DVSpawnableGhostRoleWindow : FancyWindow
|
||||||
|
{
|
||||||
|
public event Action<ProtoId<DVSpawnableGhostRolePrototype>>? RoleSelected;
|
||||||
|
|
||||||
|
private GhostRoleRulesWindow? _windowRules;
|
||||||
|
private readonly BoxContainer _entries;
|
||||||
|
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||||
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
|
private readonly List<Button> _spawnButtons = new();
|
||||||
|
private TimeSpan? _cooldownEnd;
|
||||||
|
|
||||||
|
public DVSpawnableGhostRoleWindow()
|
||||||
|
{
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
|
||||||
|
Title = Loc.GetString("ghost-gui-spawn-vent-critter-window-title");
|
||||||
|
MinSize = new Vector2(490, 400);
|
||||||
|
SetSize = new Vector2(490, 500);
|
||||||
|
|
||||||
|
_entries = new BoxContainer
|
||||||
|
{
|
||||||
|
Orientation = BoxContainer.LayoutOrientation.Vertical,
|
||||||
|
SeparationOverride = 8,
|
||||||
|
Margin = new Thickness(8),
|
||||||
|
};
|
||||||
|
|
||||||
|
var scroll = new ScrollContainer
|
||||||
|
{
|
||||||
|
HScrollEnabled = false,
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
_entries,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
ContentsContainer.AddChild(scroll);
|
||||||
|
Populate();
|
||||||
|
|
||||||
|
OnClose += () => _windowRules?.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Populate()
|
||||||
|
{
|
||||||
|
var roles = _prototype.EnumeratePrototypes<DVSpawnableGhostRolePrototype>()
|
||||||
|
.OrderBy(role => Loc.GetString(role.Name));
|
||||||
|
|
||||||
|
foreach (var role in roles)
|
||||||
|
{
|
||||||
|
var info = new GhostRoleInfoBox(Loc.GetString(role.Name), Loc.GetString(role.Description));
|
||||||
|
|
||||||
|
var button = new Button
|
||||||
|
{
|
||||||
|
Text = Loc.GetString("ghost-gui-spawn-vent-critter-spawn-button"),
|
||||||
|
VerticalAlignment = VAlignment.Center,
|
||||||
|
};
|
||||||
|
|
||||||
|
var id = new ProtoId<DVSpawnableGhostRolePrototype>(role.ID);
|
||||||
|
button.OnPressed += _ =>
|
||||||
|
{
|
||||||
|
if (IsCooldownActive())
|
||||||
|
return;
|
||||||
|
|
||||||
|
_windowRules?.Close();
|
||||||
|
|
||||||
|
_windowRules = new GhostRoleRulesWindow(Loc.GetString(role.Rules),
|
||||||
|
_ =>
|
||||||
|
{
|
||||||
|
RoleSelected?.Invoke(id);
|
||||||
|
Close();
|
||||||
|
});
|
||||||
|
_windowRules.OnClose += () =>
|
||||||
|
{
|
||||||
|
_windowRules = null;
|
||||||
|
};
|
||||||
|
_windowRules.OpenCentered();
|
||||||
|
};
|
||||||
|
|
||||||
|
_entries.AddChild(info);
|
||||||
|
_entries.AddChild(button);
|
||||||
|
_spawnButtons.Add(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_entries.ChildCount != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_entries.AddChild(new Label
|
||||||
|
{
|
||||||
|
Text = Loc.GetString("ghost-gui-spawn-vent-critter-none"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetCooldownEnd(TimeSpan? cooldownEnd)
|
||||||
|
{
|
||||||
|
_cooldownEnd = cooldownEnd;
|
||||||
|
UpdateCooldownButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void FrameUpdate(FrameEventArgs args)
|
||||||
|
{
|
||||||
|
base.FrameUpdate(args);
|
||||||
|
|
||||||
|
UpdateCooldownButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
[MemberNotNullWhen(true, nameof(_cooldownEnd))]
|
||||||
|
private bool IsCooldownActive()
|
||||||
|
{
|
||||||
|
return _cooldownEnd > _timing.CurTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateCooldownButtons()
|
||||||
|
{
|
||||||
|
if (IsCooldownActive())
|
||||||
|
{
|
||||||
|
var seconds = Math.Ceiling((_cooldownEnd.Value - _timing.CurTime).TotalSeconds);
|
||||||
|
foreach (var button in _spawnButtons)
|
||||||
|
{
|
||||||
|
button.Disabled = true;
|
||||||
|
button.Text = Loc.GetString("ghost-gui-spawn-vent-critter-spawn-cooldown", ("time", seconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var button in _spawnButtons)
|
||||||
|
{
|
||||||
|
button.Disabled = false;
|
||||||
|
button.Text = Loc.GetString("ghost-gui-spawn-vent-critter-spawn-button");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,184 @@
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using Content.Server.Administration.Logs;
|
||||||
|
using Content.Server.Mind;
|
||||||
|
using Content.Server.Roles;
|
||||||
|
using Content.Server.StationEvents.Components;
|
||||||
|
using Content.Shared._DV.CCVars;
|
||||||
|
using Content.Shared._DV.Ghost.Roles;
|
||||||
|
using Content.Shared.Database;
|
||||||
|
using Content.Shared.Ghost;
|
||||||
|
using Content.Shared.Mind.Components;
|
||||||
|
using Content.Shared.Players;
|
||||||
|
using Content.Shared.Popups;
|
||||||
|
using Content.Shared.Station.Components;
|
||||||
|
using Robust.Shared.Configuration;
|
||||||
|
using Robust.Shared.Enums;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Network;
|
||||||
|
using Robust.Shared.Player;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Server._DV.Ghost.Roles;
|
||||||
|
|
||||||
|
public sealed class DVSpawnableGhostRoleSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly MindSystem _mind = default!;
|
||||||
|
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||||
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||||
|
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||||
|
[Dependency] private readonly IAdminLogManager _adminLog = default!;
|
||||||
|
[Dependency] private readonly RoleSystem _role = default!;
|
||||||
|
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||||
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
|
[Dependency] private readonly ISharedPlayerManager _player = default!;
|
||||||
|
|
||||||
|
private readonly List<EntityCoordinates> _stationVents = new();
|
||||||
|
private readonly List<EntityCoordinates> _allVents = new();
|
||||||
|
private readonly Dictionary<NetUserId, TimeSpan> _cooldowns = new();
|
||||||
|
private TimeSpan _spawnCooldown;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
_player.PlayerStatusChanged += OnPlayerStatusChanged;
|
||||||
|
|
||||||
|
Subs.CVar(_cfg, DCCVars.VentCritterGhostRoleSpawnCooldown, value => _spawnCooldown = value < TimeSpan.Zero ? TimeSpan.Zero : value, true);
|
||||||
|
|
||||||
|
SubscribeNetworkEvent<DVSpawnableGhostRoleRequestEvent>(OnSpawnRequest);
|
||||||
|
SubscribeNetworkEvent<DVSpawnableGhostRoleCooldownRequestEvent>(OnCooldownRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Shutdown()
|
||||||
|
{
|
||||||
|
base.Shutdown();
|
||||||
|
|
||||||
|
_player.PlayerStatusChanged -= OnPlayerStatusChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs evt)
|
||||||
|
{
|
||||||
|
if (evt.NewStatus == SessionStatus.Connected)
|
||||||
|
{
|
||||||
|
SendCooldownUpdate(evt.Session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSpawnRequest(DVSpawnableGhostRoleRequestEvent msg, EntitySessionEventArgs args)
|
||||||
|
{
|
||||||
|
var session = args.SenderSession;
|
||||||
|
if (session.AttachedEntity is not { Valid: true } attached
|
||||||
|
|| !HasComp<GhostComponent>(attached))
|
||||||
|
{
|
||||||
|
_adminLog.Add(LogType.Action, LogImpact.Medium, $"{session:player} sent {nameof(DVSpawnableGhostRoleRequestEvent)} without being a ghost.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TestCooldown(session.UserId, out var cooldownEnd))
|
||||||
|
{
|
||||||
|
SendCooldownUpdate(session);
|
||||||
|
var remaining = Math.Ceiling((cooldownEnd.Value - _timing.CurTime).TotalSeconds);
|
||||||
|
_popup.PopupEntity(Loc.GetString("ghost-gui-spawn-vent-critter-cooldown-popup", ("time", remaining)), attached, attached);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_prototype.TryIndex(msg.Prototype, out var role)
|
||||||
|
|| !_prototype.TryIndex<EntityPrototype>(role.Entity, out _))
|
||||||
|
{
|
||||||
|
_adminLog.Add(LogType.Action, LogImpact.Medium, $"{session:player} tried to spawn as invalid {nameof(DVSpawnableGhostRoleRequestEvent)} prototype {msg.Prototype}.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TryPickVent(out var coords))
|
||||||
|
{
|
||||||
|
_popup.PopupEntity(Loc.GetString("ghost-gui-spawn-vent-critter-no-vents"), attached, attached);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var mob = SpawnAtPosition(role.Entity, coords);
|
||||||
|
_transform.AttachToGridOrMap(mob);
|
||||||
|
EnsureComp<MindContainerComponent>(mob);
|
||||||
|
|
||||||
|
DebugTools.AssertNotNull(session.ContentData());
|
||||||
|
|
||||||
|
if(_mind.TryGetMind(session.UserId, out _, out var mind) && !mind.IsVisitingEntity)
|
||||||
|
_mind.WipeMind(session);
|
||||||
|
|
||||||
|
var newMind = _mind.CreateMind(session.UserId, Comp<MetaDataComponent>(mob).EntityName);
|
||||||
|
|
||||||
|
_mind.SetUserId(newMind, session.UserId);
|
||||||
|
_mind.TransferTo(newMind, mob);
|
||||||
|
_role.MindAddRoles(newMind, role.MindRoles, newMind);
|
||||||
|
|
||||||
|
_cooldowns[session.UserId] = _timing.CurTime + _spawnCooldown;
|
||||||
|
SendCooldownUpdate(session);
|
||||||
|
|
||||||
|
_adminLog.Add(LogType.Action, LogImpact.Low, $"{session:player} spawned as a vent critter {msg.Prototype}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCooldownRequest(DVSpawnableGhostRoleCooldownRequestEvent msg, EntitySessionEventArgs args)
|
||||||
|
{
|
||||||
|
SendCooldownUpdate(args.SenderSession);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <param name="userId">The user to test for</param>
|
||||||
|
/// <param name="cooldownEndsAt">When the cooldown will end</param>
|
||||||
|
/// <returns>If the user is on cooldown</returns>
|
||||||
|
private bool TestCooldown(NetUserId userId, [NotNullWhen(true)] out TimeSpan? cooldownEndsAt)
|
||||||
|
{
|
||||||
|
if (_cooldowns.TryGetValue(userId, out var cooldownEnd))
|
||||||
|
{
|
||||||
|
cooldownEndsAt = cooldownEnd;
|
||||||
|
if (_timing.CurTime < cooldownEnd)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
_cooldowns.Remove(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
cooldownEndsAt = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SendCooldownUpdate(ICommonSession session)
|
||||||
|
{
|
||||||
|
TestCooldown(session.UserId, out var cooldownEnd);
|
||||||
|
RaiseNetworkEvent(new DVSpawnableGhostRoleCooldownUpdateEvent(cooldownEnd), session.Channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryPickVent(out EntityCoordinates coords)
|
||||||
|
{
|
||||||
|
_stationVents.Clear();
|
||||||
|
_allVents.Clear();
|
||||||
|
|
||||||
|
var query = EntityQueryEnumerator<VentCritterSpawnLocationComponent, TransformComponent>();
|
||||||
|
while (query.MoveNext(out _, out _, out var transform))
|
||||||
|
{
|
||||||
|
if (!transform.Anchored || !transform.Coordinates.IsValid(EntityManager))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
_allVents.Add(transform.Coordinates);
|
||||||
|
|
||||||
|
if (transform.GridUid is { } grid && HasComp<StationMemberComponent>(grid))
|
||||||
|
_stationVents.Add(transform.Coordinates);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_stationVents.Count > 0)
|
||||||
|
{
|
||||||
|
coords = _random.Pick(_stationVents);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_allVents.Count > 0)
|
||||||
|
{
|
||||||
|
coords = _random.Pick(_allVents);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
coords = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -222,6 +222,12 @@ public sealed partial class DCCVars
|
||||||
public static readonly CVarDef<bool> EnablePresetCooldowns =
|
public static readonly CVarDef<bool> EnablePresetCooldowns =
|
||||||
CVarDef.Create("game.enable_preset_cooldowns", false, CVar.SERVERONLY);
|
CVarDef.Create("game.enable_preset_cooldowns", false, CVar.SERVERONLY);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of time a player must wait after spawning as a vent critter ghost role before spawning as another one
|
||||||
|
/// </summary>
|
||||||
|
public static readonly CVarDef<TimeSpan> VentCritterGhostRoleSpawnCooldown =
|
||||||
|
CVarDef.Create("ghost.vent_critter_spawn_cooldown", TimeSpan.FromMinutes(3), CVar.SERVERONLY);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A string containing a list of newline-separated strings to be highlighted in the chat. Use this instead of Wizden's CVar.
|
/// A string containing a list of newline-separated strings to be highlighted in the chat. Use this instead of Wizden's CVar.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared._DV.Ghost.Roles;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class DVSpawnableGhostRoleRequestEvent(ProtoId<DVSpawnableGhostRolePrototype> prototype) : EntityEventArgs
|
||||||
|
{
|
||||||
|
public ProtoId<DVSpawnableGhostRolePrototype> Prototype = prototype;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class DVSpawnableGhostRoleCooldownRequestEvent : EntityEventArgs;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class DVSpawnableGhostRoleCooldownUpdateEvent(TimeSpan? cooldownEnd) : EntityEventArgs
|
||||||
|
{
|
||||||
|
public TimeSpan? CooldownEnd = cooldownEnd;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Shared._DV.Ghost.Roles;
|
||||||
|
|
||||||
|
[Prototype("dvSpawnableGhostRole")]
|
||||||
|
public sealed partial class DVSpawnableGhostRolePrototype : IPrototype
|
||||||
|
{
|
||||||
|
[IdDataField]
|
||||||
|
public string ID { get; private set; } = default!;
|
||||||
|
|
||||||
|
public LocId Name => $"dv-spawnable-{ID}.name";
|
||||||
|
|
||||||
|
public LocId Description => $"dv-spawnable-{ID}.description";
|
||||||
|
|
||||||
|
[DataField(required: true)]
|
||||||
|
public LocId Rules;
|
||||||
|
|
||||||
|
[DataField]
|
||||||
|
public List<EntProtoId> MindRoles = new() { "MindRoleGhostRoleNeutral" };
|
||||||
|
|
||||||
|
[DataField(required: true)]
|
||||||
|
public EntProtoId Entity;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
ghost-gui-spawn-vent-critter-window-title = Spawn as Vent Critter
|
||||||
|
ghost-gui-spawn-vent-critter-spawn-button = Spawn
|
||||||
|
ghost-gui-spawn-vent-critter-spawn-cooldown = Spawn ({$time}s)
|
||||||
|
ghost-gui-spawn-vent-critter-none = There are no vent critters available.
|
||||||
|
ghost-gui-spawn-vent-critter-no-vents = No suitable vents were found.
|
||||||
|
ghost-gui-spawn-vent-critter-cooldown-popup = You can spawn as a vent critter again in {$time}s.
|
||||||
|
|
||||||
|
ghost-gui-spawn-vent-critter-button = Vent Critter
|
||||||
|
|
||||||
|
dv-spawnable-MobMouse =
|
||||||
|
.name = Mouse
|
||||||
|
.description = A hungry and mischievous mouse.
|
||||||
|
|
||||||
|
dv-spawnable-MobMothroach =
|
||||||
|
.name = Mothroach
|
||||||
|
.description = A cute but mischievous mothroach.
|
||||||
|
|
||||||
|
dv-spawnable-MobSnail =
|
||||||
|
.name = Snail
|
||||||
|
.description = A little snail who doesn't mind a bit of space. Just stay on grid!
|
||||||
|
|
||||||
|
dv-spawnable-MobSnailSpeed =
|
||||||
|
.name = Speed Snail
|
||||||
|
.description = A little snail with snailborn thrusters.
|
||||||
|
|
||||||
|
dv-spawnable-MobSnailMoth =
|
||||||
|
.name = Snoth
|
||||||
|
.description = A little snoth who doesn't mind a bit of space. Just stay on grid!
|
||||||
|
|
||||||
|
dv-spawnable-MobDionaNymph =
|
||||||
|
.name = Diona Nymph
|
||||||
|
.description = A lone diona nymph. Do whatever it is that nymphs do.
|
||||||
|
|
@ -495,15 +495,17 @@
|
||||||
id: MobMothroach
|
id: MobMothroach
|
||||||
description: This is the adorable by-product of multiple attempts at genetically mixing mothpeople with cockroaches.
|
description: This is the adorable by-product of multiple attempts at genetically mixing mothpeople with cockroaches.
|
||||||
components:
|
components:
|
||||||
- type: GhostRole
|
# Begin DeltaV Removals - players can spawn in as these at will
|
||||||
makeSentient: true
|
# - type: GhostRole
|
||||||
allowSpeech: true
|
# makeSentient: true
|
||||||
allowMovement: true
|
# allowSpeech: true
|
||||||
name: ghost-role-information-mothroach-name
|
# allowMovement: true
|
||||||
description: ghost-role-information-mothroach-description
|
# name: ghost-role-information-mothroach-name
|
||||||
rules: ghost-role-information-freeagent-rules
|
# description: ghost-role-information-mothroach-description
|
||||||
mindRoles:
|
# rules: ghost-role-information-freeagent-rules
|
||||||
- MindRoleGhostRoleFreeAgentHarmless
|
# mindRoles:
|
||||||
|
# - MindRoleGhostRoleFreeAgentHarmless
|
||||||
|
# End DeltaV Removals - players can spawn in as these at will
|
||||||
- type: Fixtures
|
- type: Fixtures
|
||||||
fixtures:
|
fixtures:
|
||||||
fix1:
|
fix1:
|
||||||
|
|
@ -515,7 +517,7 @@
|
||||||
- SmallMobMask
|
- SmallMobMask
|
||||||
layer:
|
layer:
|
||||||
- SmallMobLayer
|
- SmallMobLayer
|
||||||
- type: GhostTakeoverAvailable
|
# - type: GhostTakeoverAvailable # DeltaV - players can spawn in as these at will
|
||||||
- type: Speech
|
- type: Speech
|
||||||
speechVerb: Moth
|
speechVerb: Moth
|
||||||
speechSounds: Chitter # Delta-V - Eep!
|
speechSounds: Chitter # Delta-V - Eep!
|
||||||
|
|
@ -1838,16 +1840,18 @@
|
||||||
id: MobMouse
|
id: MobMouse
|
||||||
description: Squeak!
|
description: Squeak!
|
||||||
components:
|
components:
|
||||||
- type: GhostRole
|
# Begin DeltaV Removals - players can spawn in as these at will
|
||||||
makeSentient: true
|
# - type: GhostRole
|
||||||
allowSpeech: true
|
# makeSentient: true
|
||||||
allowMovement: true
|
# allowSpeech: true
|
||||||
name: ghost-role-information-mouse-name
|
# allowMovement: true
|
||||||
description: ghost-role-information-mouse-description
|
# name: ghost-role-information-mouse-name
|
||||||
rules: ghost-role-information-freeagent-rules
|
# description: ghost-role-information-mouse-description
|
||||||
mindRoles:
|
# rules: ghost-role-information-freeagent-rules
|
||||||
- MindRoleGhostRoleFreeAgentHarmless
|
# mindRoles:
|
||||||
- type: GhostTakeoverAvailable
|
# - MindRoleGhostRoleFreeAgentHarmless
|
||||||
|
# - type: GhostTakeoverAvailable
|
||||||
|
# End DeltaV Removals - players can spawn in as these at will
|
||||||
- type: Speech
|
- type: Speech
|
||||||
speechSounds: Squeak
|
speechSounds: Squeak
|
||||||
speechVerb: SmallMob
|
speechVerb: SmallMob
|
||||||
|
|
|
||||||
|
|
@ -387,16 +387,18 @@
|
||||||
name: snail
|
name: snail
|
||||||
description: Revolting unless you're french.
|
description: Revolting unless you're french.
|
||||||
components:
|
components:
|
||||||
- type: GhostRole
|
# Begin DeltaV Removals - players can spawn in as these at will
|
||||||
makeSentient: true
|
# - type: GhostRole
|
||||||
allowSpeech: false
|
# makeSentient: true
|
||||||
allowMovement: true
|
# allowSpeech: false
|
||||||
name: ghost-role-information-snail-name
|
# allowMovement: true
|
||||||
description: ghost-role-information-snail-description
|
# name: ghost-role-information-snail-name
|
||||||
rules: ghost-role-information-freeagent-rules
|
# description: ghost-role-information-snail-description
|
||||||
mindRoles:
|
# rules: ghost-role-information-freeagent-rules
|
||||||
- MindRoleGhostRoleFreeAgentHarmless
|
# mindRoles:
|
||||||
- type: GhostTakeoverAvailable
|
# - MindRoleGhostRoleFreeAgentHarmless
|
||||||
|
# - type: GhostTakeoverAvailable
|
||||||
|
# End DeltaV Removals - players can spawn in as these at will
|
||||||
- type: Emoting
|
- type: Emoting
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
drawdepth: SmallMobs
|
drawdepth: SmallMobs
|
||||||
|
|
@ -552,10 +554,12 @@
|
||||||
id: MobSnailSpeed
|
id: MobSnailSpeed
|
||||||
suffix: Speed
|
suffix: Speed
|
||||||
components:
|
components:
|
||||||
- type: GhostRole
|
# Begin DeltaV Removals - players can spawn in as these at will
|
||||||
name: ghost-role-information-snailspeed-name
|
# - type: GhostRole
|
||||||
description: ghost-role-information-snailspeed-description
|
# name: ghost-role-information-snailspeed-name
|
||||||
rules: ghost-role-information-freeagent-rules
|
# description: ghost-role-information-snailspeed-description
|
||||||
|
# rules: ghost-role-information-freeagent-rules
|
||||||
|
# End DeltaV Removals - players can spawn in as these at will
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
layers:
|
layers:
|
||||||
- map: ["enum.DamageStateVisualLayers.Base"]
|
- map: ["enum.DamageStateVisualLayers.Base"]
|
||||||
|
|
@ -579,10 +583,12 @@
|
||||||
id: MobSnailMoth
|
id: MobSnailMoth
|
||||||
name: Snoth
|
name: Snoth
|
||||||
components:
|
components:
|
||||||
- type: GhostRole
|
# Begin DeltaV Removals - players can spawn in as these at will
|
||||||
name: ghost-role-information-snoth-name
|
# - type: GhostRole
|
||||||
description: ghost-role-information-snoth-description
|
# name: ghost-role-information-snoth-name
|
||||||
rules: ghost-role-information-freeagent-rules
|
# description: ghost-role-information-snoth-description
|
||||||
|
# rules: ghost-role-information-freeagent-rules
|
||||||
|
# End DeltaV Removals - players can spawn in as these at will
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
layers:
|
layers:
|
||||||
- map: ["enum.DamageStateVisualLayers.Base"]
|
- map: ["enum.DamageStateVisualLayers.Base"]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
- type: dvSpawnableGhostRole
|
||||||
|
id: MobMouse
|
||||||
|
entity: MobMouse
|
||||||
|
rules: ghost-role-information-freeagent-rules
|
||||||
|
mindRoles:
|
||||||
|
- MindRoleGhostRoleFreeAgentHarmless
|
||||||
|
|
||||||
|
- type: dvSpawnableGhostRole
|
||||||
|
id: MobMothroach
|
||||||
|
entity: MobMothroach
|
||||||
|
rules: ghost-role-information-freeagent-rules
|
||||||
|
mindRoles:
|
||||||
|
- MindRoleGhostRoleFreeAgentHarmless
|
||||||
|
|
||||||
|
- type: dvSpawnableGhostRole
|
||||||
|
id: MobSnail
|
||||||
|
entity: MobSnail
|
||||||
|
rules: ghost-role-information-freeagent-rules
|
||||||
|
mindRoles:
|
||||||
|
- MindRoleGhostRoleFreeAgentHarmless
|
||||||
|
|
||||||
|
- type: dvSpawnableGhostRole
|
||||||
|
id: MobSnailSpeed
|
||||||
|
entity: MobSnailSpeed
|
||||||
|
rules: ghost-role-information-freeagent-rules
|
||||||
|
mindRoles:
|
||||||
|
- MindRoleGhostRoleFreeAgentHarmless
|
||||||
|
|
||||||
|
- type: dvSpawnableGhostRole
|
||||||
|
id: MobSnailMoth
|
||||||
|
entity: MobSnailMoth
|
||||||
|
rules: ghost-role-information-freeagent-rules
|
||||||
|
mindRoles:
|
||||||
|
- MindRoleGhostRoleFreeAgentHarmless
|
||||||
|
|
||||||
|
- type: dvSpawnableGhostRole
|
||||||
|
id: MobDionaNymph
|
||||||
|
entity: MobDionaNymph
|
||||||
|
rules: ghost-role-information-nonantagonist-rules
|
||||||
Loading…
Reference in New Issue