-
Notifications
You must be signed in to change notification settings - Fork 636
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1484 from space-syndicate/upstream-sync
Upstream sync
- Loading branch information
Showing
143 changed files
with
42,088 additions
and
65,996 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
using Content.Shared.StatusIcon; | ||
using Content.Shared.StatusIcon.Components; | ||
using Robust.Shared.Prototypes; | ||
using Content.Shared.Ghost; | ||
using Robust.Client.Player; | ||
|
||
namespace Content.Client.Antag; | ||
|
||
/// <summary> | ||
/// Used for assigning specified icons for antags. | ||
/// </summary> | ||
public abstract class AntagStatusIconSystem<T> : SharedStatusIconSystem | ||
where T : Component | ||
{ | ||
[Dependency] private readonly IPrototypeManager _prototype = default!; | ||
[Dependency] private readonly IPlayerManager _player = default!; | ||
|
||
/// <summary> | ||
/// Will check if the local player has the same component as the one who called it and give the status icon. | ||
/// </summary> | ||
/// <param name="antagStatusIcon">The status icon that your antag uses</param> | ||
/// <param name="args">The GetStatusIcon event.</param> | ||
protected virtual void GetStatusIcon(string antagStatusIcon, ref GetStatusIconsEvent args) | ||
{ | ||
var ent = _player.LocalPlayer?.ControlledEntity; | ||
|
||
if (!HasComp<T>(ent) && !HasComp<GhostComponent>(ent)) | ||
return; | ||
|
||
args.StatusIcons.Add(_prototype.Index<StatusIconPrototype>(antagStatusIcon)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
using Content.Shared.Revolutionary.Components; | ||
using Content.Client.Antag; | ||
using Content.Shared.StatusIcon.Components; | ||
|
||
namespace Content.Client.Revolutionary; | ||
|
||
/// <summary> | ||
/// Used for the client to get status icons from other revs. | ||
/// </summary> | ||
public sealed class RevolutionarySystem : AntagStatusIconSystem<RevolutionaryComponent> | ||
{ | ||
public override void Initialize() | ||
{ | ||
base.Initialize(); | ||
|
||
SubscribeLocalEvent<RevolutionaryComponent, GetStatusIconsEvent>(GetRevIcon); | ||
SubscribeLocalEvent<HeadRevolutionaryComponent, GetStatusIconsEvent>(GetHeadRevIcon); | ||
} | ||
|
||
/// <summary> | ||
/// Checks if the person who triggers the GetStatusIcon event is also a Rev or a HeadRev. | ||
/// </summary> | ||
private void GetRevIcon(EntityUid uid, RevolutionaryComponent comp, ref GetStatusIconsEvent args) | ||
{ | ||
if (!HasComp<HeadRevolutionaryComponent>(uid)) | ||
{ | ||
GetStatusIcon(comp.RevStatusIcon, ref args); | ||
} | ||
} | ||
|
||
private void GetHeadRevIcon(EntityUid uid, HeadRevolutionaryComponent comp, ref GetStatusIconsEvent args) | ||
{ | ||
GetStatusIcon(comp.HeadRevStatusIcon, ref args); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,237 @@ | ||
using Content.Server.GameTicking.Rules; | ||
using Content.Server.GameTicking.Rules.Components; | ||
using Content.Server.Roles.Jobs; | ||
using Content.Server.Preferences.Managers; | ||
using Content.Shared.Humanoid; | ||
using Content.Shared.Preferences; | ||
using Robust.Server.Player; | ||
using System.Linq; | ||
using Content.Server.Mind; | ||
using Robust.Shared.Random; | ||
using Robust.Shared.Map; | ||
using System.Numerics; | ||
using Content.Shared.Inventory; | ||
using Content.Server.Storage.EntitySystems; | ||
using Robust.Shared.Audio; | ||
using Robust.Server.GameObjects; | ||
using Content.Server.Chat.Managers; | ||
using Content.Server.GameTicking; | ||
using Robust.Shared.Containers; | ||
using Content.Shared.Mobs.Components; | ||
using Content.Server.Station.Systems; | ||
using Content.Server.Shuttles.Systems; | ||
using Content.Shared.Mobs; | ||
using Robust.Server.Containers; | ||
using Robust.Shared.Prototypes; | ||
|
||
namespace Content.Server.Antag; | ||
|
||
public sealed class AntagSelectionSystem : GameRuleSystem<GameRuleComponent> | ||
{ | ||
[Dependency] private readonly IChatManager _chatManager = default!; | ||
[Dependency] private readonly IServerPreferencesManager _prefs = default!; | ||
[Dependency] private readonly IPlayerManager _playerSystem = default!; | ||
[Dependency] private readonly IRobustRandom _random = default!; | ||
[Dependency] private readonly AudioSystem _audioSystem = default!; | ||
[Dependency] private readonly ContainerSystem _containerSystem = default!; | ||
[Dependency] private readonly JobSystem _jobs = default!; | ||
[Dependency] private readonly MindSystem _mindSystem = default!; | ||
[Dependency] private readonly InventorySystem _inventory = default!; | ||
[Dependency] private readonly StorageSystem _storageSystem = default!; | ||
[Dependency] private readonly StationSystem _stationSystem = default!; | ||
[Dependency] private readonly EmergencyShuttleSystem _emergencyShuttle = default!; | ||
|
||
/// <summary> | ||
/// Attempts to start the game rule by checking if there are enough players in lobby and readied. | ||
/// </summary> | ||
/// <param name="ev">The roundstart attempt event</param> | ||
/// <param name="uid">The entity the gamerule you are using is on</param> | ||
/// <param name="minPlayers">The minimum amount of players needed for you gamerule to start.</param> | ||
/// <param name="gameRule">The gamerule component.</param> | ||
|
||
public void AttemptStartGameRule(RoundStartAttemptEvent ev, EntityUid uid, int minPlayers, GameRuleComponent gameRule) | ||
{ | ||
if (GameTicker.IsGameRuleAdded(uid, gameRule)) | ||
{ | ||
if (!ev.Forced && ev.Players.Length < minPlayers) | ||
{ | ||
_chatManager.SendAdminAnnouncement(Loc.GetString("rev-not-enough-ready-players", | ||
("readyPlayersCount", ev.Players.Length), | ||
("minimumPlayers", minPlayers))); | ||
ev.Cancel(); | ||
} | ||
else if (ev.Players.Length == 0) | ||
{ | ||
_chatManager.DispatchServerAnnouncement(Loc.GetString("rev-no-one-ready")); | ||
ev.Cancel(); | ||
} | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Will check which players are eligible to be chosen for antagonist and give them the given antag. | ||
/// </summary> | ||
/// <param name="antagPrototype">The antag prototype from your rule component.</param> | ||
/// <param name="maxAntags">How many antags can be present in any given round.</param> | ||
/// <param name="antagsPerPlayer">How many players you need to spawn an additional antag.</param> | ||
/// <param name="antagSound">The intro sound that plays when the antag is chosen.</param> | ||
/// <param name="antagGreeting">The antag message you want shown when the antag is chosen.</param> | ||
/// <param name="greetingColor">The color of the message for the antag greeting in hex.</param> | ||
/// <param name="chosen">A list of all the antags chosen in case you need to add stuff after.</param> | ||
/// <param name="includeHeads">Whether or not heads can be chosen as antags for this gamemode.</param> | ||
public void EligiblePlayers(string antagPrototype, | ||
int maxAntags, | ||
int antagsPerPlayer, | ||
SoundSpecifier? antagSound, | ||
string antagGreeting, | ||
string greetingColor, | ||
out List<EntityUid> chosen, | ||
bool includeHeads = false) | ||
{ | ||
var allPlayers = _playerSystem.ServerSessions.ToList(); | ||
var playerList = new List<IPlayerSession>(); | ||
var prefList = new List<IPlayerSession>(); | ||
chosen = new List<EntityUid>(); | ||
foreach (var player in allPlayers) | ||
{ | ||
if (includeHeads == false) | ||
{ | ||
if (!_jobs.CanBeAntag(player)) | ||
continue; | ||
} | ||
|
||
if (player.AttachedEntity == null || HasComp<HumanoidAppearanceComponent>(player.AttachedEntity)) | ||
playerList.Add(player); | ||
else | ||
continue; | ||
|
||
var pref = (HumanoidCharacterProfile) _prefs.GetPreferences(player.UserId).SelectedCharacter; | ||
if (pref.AntagPreferences.Contains(antagPrototype)) | ||
prefList.Add(player); | ||
} | ||
|
||
if (playerList.Count == 0) | ||
return; | ||
|
||
var antags = Math.Clamp(allPlayers.Count / antagsPerPlayer, 1, maxAntags); | ||
for (var antag = 0; antag < antags; antag++) | ||
{ | ||
IPlayerSession chosenPlayer; | ||
if (prefList.Count == 0) | ||
{ | ||
if (playerList.Count == 0) | ||
{ | ||
break; | ||
} | ||
chosenPlayer = _random.PickAndTake(playerList); | ||
} | ||
else | ||
{ | ||
chosenPlayer = _random.PickAndTake(prefList); | ||
playerList.Remove(chosenPlayer); | ||
} | ||
|
||
if (!_mindSystem.TryGetMind(chosenPlayer, out _, out var mind) || | ||
mind.OwnedEntity is not { } ownedEntity) | ||
{ | ||
continue; | ||
} | ||
|
||
chosen.Add(ownedEntity); | ||
_audioSystem.PlayGlobal(antagSound, ownedEntity); | ||
if (mind.Session != null) | ||
{ | ||
var message = Loc.GetString(antagGreeting); | ||
var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", message)); | ||
_chatManager.ChatMessageToOne(Shared.Chat.ChatChannel.Server, message, wrappedMessage, default, false, mind.Session.ConnectedClient, Color.FromHex(greetingColor)); | ||
} | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Will take a group of entities and check if they are all alive or dead | ||
/// </summary> | ||
/// <param name="list">The list of the entities</param> | ||
/// <param name="checkOffStation">Bool for if you want to check if someone is in space and consider them dead. (Won't check when emergency shuttle arrives just in case)</param> | ||
/// <returns></returns> | ||
public bool IsGroupDead(List<EntityUid> list, bool checkOffStation) | ||
{ | ||
var dead = 0; | ||
foreach (var entity in list) | ||
{ | ||
if (TryComp<MobStateComponent>(entity, out var state)) | ||
{ | ||
if (state.CurrentState == MobState.Dead || state.CurrentState == MobState.Invalid) | ||
{ | ||
dead++; | ||
} | ||
else if (checkOffStation && _stationSystem.GetOwningStation(entity) == null && !_emergencyShuttle.EmergencyShuttleArrived) | ||
{ | ||
dead++; | ||
} | ||
} | ||
//If they don't have the MobStateComponent they might as well be dead. | ||
else | ||
{ | ||
dead++; | ||
} | ||
} | ||
|
||
return dead == list.Count || list.Count == 0; | ||
} | ||
|
||
/// <summary> | ||
/// Will attempt to spawn an item inside of a persons bag and then pockets. | ||
/// </summary> | ||
/// <param name="antag">The entity that you want to spawn an item on</param> | ||
/// <param name="items">A list of prototype IDs that you want to spawn in the bag.</param> | ||
public void GiveAntagBagGear(EntityUid antag, List<EntProtoId> items) | ||
{ | ||
foreach (var item in items) | ||
{ | ||
GiveAntagBagGear(antag, item); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Will attempt to spawn an item inside of a persons bag and then pockets. | ||
/// </summary> | ||
/// <param name="antag">The entity that you want to spawn an item on</param> | ||
/// <param name="item">The prototype ID that you want to spawn in the bag.</param> | ||
public void GiveAntagBagGear(EntityUid antag, string item) | ||
{ | ||
var itemToSpawn = Spawn(item, new EntityCoordinates(antag, Vector2.Zero)); | ||
if (!_inventory.TryGetSlotContainer(antag, "back", out var backSlot, out _)) | ||
return; | ||
|
||
var bag = backSlot.ContainedEntity; | ||
if (bag != null && HasComp<ContainerManagerComponent>(bag) && _storageSystem.CanInsert(bag.Value, itemToSpawn, out _)) | ||
{ | ||
_storageSystem.Insert(bag.Value, itemToSpawn, out _); | ||
} | ||
else if (_inventory.TryGetSlotContainer(antag, "jumpsuit", out var jumpsuit, out _) && jumpsuit.ContainedEntity != null) | ||
{ | ||
if (_inventory.TryGetSlotContainer(antag, "pocket1", out var pocket1Slot, out _)) | ||
{ | ||
if (pocket1Slot.ContainedEntity == null) | ||
{ | ||
if (_containerSystem.CanInsert(itemToSpawn, pocket1Slot)) | ||
{ | ||
pocket1Slot.Insert(itemToSpawn); | ||
} | ||
} | ||
else if (_inventory.TryGetSlotContainer(antag, "pocket2", out var pocket2Slot, out _)) | ||
{ | ||
if (pocket2Slot.ContainedEntity == null) | ||
{ | ||
if (_containerSystem.CanInsert(itemToSpawn, pocket2Slot)) | ||
{ | ||
pocket2Slot.Insert(itemToSpawn); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
Oops, something went wrong.