Respawn button (#1702)
* Add respawn action for ghosts with confirmation popup Introduces a new respawn action for ghosts, allowing them to return to the lobby and respawn as a new character. Adds supporting systems, localization, prototype definitions, and an icon for the action. The action includes a confirmation popup to prevent accidental use. * Prevent respawning with the same character in a round Introduces a system to block players from rejoining a round with a character that has already died, notifying them with a localized message. Refactors and moves respawn-related localization strings to new files for both English and Russian. * Prevent admins from character reuse restriction Added a check in CP14RespawnSystem to allow admins to bypass the restriction that prevents the same character from re-entering the round. This ensures that admin users are not limited by the character reuse logic.
This commit is contained in:
@@ -136,6 +136,7 @@ namespace Content.Client.Ghost
|
||||
//_actions.RemoveAction(uid, component.ToggleGhostHearingActionEntity); //Dont need in CP14
|
||||
//CP14
|
||||
_actions.RemoveAction(uid, component.CP14ToggleRoofActionEntity);
|
||||
_actions.RemoveAction(uid, component.CP14RespawnActionEntity);
|
||||
//CP14 end
|
||||
|
||||
if (uid != _playerManager.LocalEntity)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Server._CP14.Respawn;
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Server.Administration.Systems;
|
||||
using Content.Server.GameTicking.Events;
|
||||
@@ -212,6 +213,13 @@ namespace Content.Server.GameTicking
|
||||
character = HumanoidCharacterProfile.RandomWithSpecies(speciesId);
|
||||
}
|
||||
|
||||
//CP14 blocking rejoining to game with same character
|
||||
var CP14Ev = new CP14PlayerSpawnAttemptEvent(player, character, jobId, lateJoin, station);
|
||||
RaiseLocalEvent(CP14Ev);
|
||||
if (CP14Ev.Cancelled)
|
||||
return;
|
||||
//CP14 end
|
||||
|
||||
// We raise this event to allow other systems to handle spawning this player themselves. (e.g. late-join wizard, etc)
|
||||
var bev = new PlayerBeforeSpawnEvent(player, character, jobId, lateJoin, station);
|
||||
RaiseLocalEvent(bev);
|
||||
|
||||
@@ -224,18 +224,19 @@ namespace Content.Server.Ghost
|
||||
|
||||
// Entity can't see ghosts anymore.
|
||||
_eye.RefreshVisibilityMask(uid);
|
||||
_actions.RemoveAction(uid, component.BooActionEntity);
|
||||
//_actions.RemoveAction(uid, component.BooActionEntity); //Dont need in CP14
|
||||
}
|
||||
|
||||
private void OnMapInit(EntityUid uid, GhostComponent component, MapInitEvent args)
|
||||
{
|
||||
_actions.AddAction(uid, ref component.BooActionEntity, component.BooAction);
|
||||
//_actions.AddAction(uid, ref component.BooActionEntity, component.BooAction); //Dont need in CP14
|
||||
//_actions.AddAction(uid, ref component.ToggleGhostHearingActionEntity, component.ToggleGhostHearingAction); //Dont need in CP14
|
||||
_actions.AddAction(uid, ref component.ToggleLightingActionEntity, component.ToggleLightingAction);
|
||||
_actions.AddAction(uid, ref component.ToggleFoVActionEntity, component.ToggleFoVAction);
|
||||
_actions.AddAction(uid, ref component.ToggleGhostsActionEntity, component.ToggleGhostsAction);
|
||||
//CP14
|
||||
_actions.AddAction(uid, ref component.CP14ToggleRoofActionEntity, component.CP14ToggleRoofAction);
|
||||
_actions.AddAction(uid, ref component.CP14RespawnActionEntity, component.CP14RespawnAction);
|
||||
//CP14 end
|
||||
}
|
||||
|
||||
|
||||
97
Content.Server/_CP14/Respawn/CP14RespawnSystem.cs
Normal file
97
Content.Server/_CP14/Respawn/CP14RespawnSystem.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.GameTicking.Events;
|
||||
using Content.Shared._CP14.Respawn;
|
||||
using Content.Shared.Administration.Managers;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Ghost;
|
||||
using Content.Shared.Preferences;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Server._CP14.Respawn;
|
||||
|
||||
public sealed partial class CP14RespawnSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly GameTicker _gameTicker = default!;
|
||||
[Dependency] private readonly ChatSystem _chat = default!;
|
||||
[Dependency] private readonly ISharedAdminManager _admin = default!;
|
||||
|
||||
/// <summary>
|
||||
/// We keep a list of all characters connected in a round to prevent the same character from re-entering the round.
|
||||
/// Of course, we only check by character name, and this is easy to circumvent by renaming the character,
|
||||
/// but the main goal here is to make the player understand that this is not the right thing to do.
|
||||
/// If a player abuses the ability to re-enter a round, the administration will take care of it.
|
||||
/// </summary>
|
||||
private Dictionary<NetUserId, List<string>> _usedCharacters = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<GhostComponent, CP14RespawnAction>(OnRespawnAction);
|
||||
SubscribeLocalEvent<CP14PlayerSpawnAttemptEvent>(OnBeforePlayerSpawn);
|
||||
SubscribeLocalEvent<RoundStartingEvent>(OnRoundEnd);
|
||||
}
|
||||
|
||||
private void OnRoundEnd(RoundStartingEvent ev)
|
||||
{
|
||||
_usedCharacters.Clear();
|
||||
}
|
||||
|
||||
private void OnBeforePlayerSpawn(CP14PlayerSpawnAttemptEvent ev)
|
||||
{
|
||||
if (_admin.IsAdmin(ev.Player, true))
|
||||
return;
|
||||
|
||||
if (!_usedCharacters.TryGetValue(ev.Player.UserId, out var usedCharacters))
|
||||
{
|
||||
usedCharacters = [];
|
||||
_usedCharacters[ev.Player.UserId] = usedCharacters;
|
||||
}
|
||||
|
||||
if (usedCharacters.Contains(ev.Profile.Name))
|
||||
{
|
||||
var filter = Filter.Empty().AddPlayer(ev.Player);
|
||||
_chat.DispatchFilteredAnnouncement(
|
||||
filter,
|
||||
Loc.GetString("cp14-respawn-same-char-bloked"),
|
||||
colorOverride: Color.Red,
|
||||
announcementSound: new SoundPathSpecifier("/Audio/Effects/beep1.ogg"),
|
||||
sender: "Server");
|
||||
ev.Cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
usedCharacters.Add(ev.Profile.Name);
|
||||
}
|
||||
|
||||
private void OnRespawnAction(Entity<GhostComponent> ent, ref CP14RespawnAction args)
|
||||
{
|
||||
if (!TryComp<ActorComponent>(ent, out var actor))
|
||||
return;
|
||||
|
||||
_gameTicker.Respawn(actor.PlayerSession);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called before a player spawns for a specific character. Can be canceled by prohibiting connection for that character.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public sealed class CP14PlayerSpawnAttemptEvent(
|
||||
ICommonSession player,
|
||||
HumanoidCharacterProfile profile,
|
||||
string? jobId,
|
||||
bool lateJoin,
|
||||
EntityUid station)
|
||||
: CancellableEntityEventArgs
|
||||
{
|
||||
public ICommonSession Player { get; } = player;
|
||||
public HumanoidCharacterProfile Profile { get; } = profile;
|
||||
public string? JobId { get; } = jobId;
|
||||
public bool LateJoin { get; } = lateJoin;
|
||||
public EntityUid Station { get; } = station;
|
||||
}
|
||||
@@ -47,8 +47,14 @@ public sealed partial class GhostComponent : Component
|
||||
[DataField]
|
||||
public EntProtoId CP14ToggleRoofAction = "CP14ActionToggleRoofs";
|
||||
|
||||
[DataField]
|
||||
public EntProtoId CP14RespawnAction = "CP14ActionRespawn";
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public EntityUid? CP14ToggleRoofActionEntity;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public EntityUid? CP14RespawnActionEntity;
|
||||
//CP14 Ghost entities end
|
||||
|
||||
// End actions
|
||||
|
||||
7
Content.Shared/_CP14/Respawn/CP14SharedRespawnSystem.cs
Normal file
7
Content.Shared/_CP14/Respawn/CP14SharedRespawnSystem.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using Content.Shared.Actions;
|
||||
|
||||
namespace Content.Shared._CP14.Respawn;
|
||||
|
||||
public sealed partial class CP14RespawnAction : InstantActionEvent
|
||||
{
|
||||
}
|
||||
3
Resources/Locale/en-US/_CP14/respawn.ftl
Normal file
3
Resources/Locale/en-US/_CP14/respawn.ftl
Normal file
@@ -0,0 +1,3 @@
|
||||
cp14-respawn-action-popup = Are you sure you want to return to the lobby? You will no longer be able to play as this character!
|
||||
|
||||
cp14-respawn-same-char-bloked = This character has died. You can no longer use them in this round.
|
||||
3
Resources/Locale/ru-RU/_CP14/respawn.ftl
Normal file
3
Resources/Locale/ru-RU/_CP14/respawn.ftl
Normal file
@@ -0,0 +1,3 @@
|
||||
cp14-respawn-action-popup = Вы уверены что хотите вернуться в лобби? Игра за этого персонажа станет недоступной!
|
||||
|
||||
cp14-respawn-same-char-bloked = Этот персонаж погиб. Вы больше не можете использовать его в этом раунде.
|
||||
@@ -38,3 +38,20 @@
|
||||
- type: InstantAction
|
||||
event: !type:CP14ToggleRoofVisibilityAction
|
||||
|
||||
- type: entity
|
||||
id: CP14ActionRespawn
|
||||
name: Respawn
|
||||
description: Return to the lobby to connect with a new character.
|
||||
components:
|
||||
- type: Action
|
||||
priority: 20
|
||||
startDelay: true
|
||||
useDelay: 300
|
||||
checkCanInteract: false
|
||||
icon:
|
||||
sprite: _CP14/Actions/zLevel.rsi
|
||||
state: respawn
|
||||
- type: ConfirmableAction
|
||||
popup: cp14-respawn-action-popup
|
||||
- type: InstantAction
|
||||
event: !type:CP14RespawnAction
|
||||
@@ -15,6 +15,9 @@
|
||||
},
|
||||
{
|
||||
"name": "roof"
|
||||
},
|
||||
{
|
||||
"name": "respawn"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
BIN
Resources/Textures/_CP14/Actions/zLevel.rsi/respawn.png
Normal file
BIN
Resources/Textures/_CP14/Actions/zLevel.rsi/respawn.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 550 B |
Reference in New Issue
Block a user