Revolutionaries (#18477)
Co-authored-by: coolmankid12345 <coolmankid12345@users.noreply.github.com> Co-authored-by: EmoGarbage404 <retron404@gmail.com>
This commit is contained in:
32
Content.Client/Antag/AntagStatusIconSystem.cs
Normal file
32
Content.Client/Antag/AntagStatusIconSystem.cs
Normal file
@@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
35
Content.Client/Revolutionary/RevolutionarySystem.cs
Normal file
35
Content.Client/Revolutionary/RevolutionarySystem.cs
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +1,14 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Content.Client.Antag;
|
||||||
using Content.Shared.Humanoid;
|
using Content.Shared.Humanoid;
|
||||||
using Content.Shared.StatusIcon;
|
|
||||||
using Content.Shared.StatusIcon.Components;
|
using Content.Shared.StatusIcon.Components;
|
||||||
using Content.Shared.Zombies;
|
using Content.Shared.Zombies;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Client.Player;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
|
|
||||||
namespace Content.Client.Zombies;
|
namespace Content.Client.Zombies;
|
||||||
|
|
||||||
public sealed class ZombieSystem : SharedZombieSystem
|
public sealed class ZombieSystem : AntagStatusIconSystem<ZombieComponent>
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IPlayerManager _player = default!;
|
|
||||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -38,9 +34,6 @@ public sealed class ZombieSystem : SharedZombieSystem
|
|||||||
|
|
||||||
private void OnGetStatusIcon(EntityUid uid, ZombieComponent component, ref GetStatusIconsEvent args)
|
private void OnGetStatusIcon(EntityUid uid, ZombieComponent component, ref GetStatusIconsEvent args)
|
||||||
{
|
{
|
||||||
if (!HasComp<ZombieComponent>(_player.LocalPlayer?.ControlledEntity))
|
GetStatusIcon(component.ZombieStatusIcon, ref args);
|
||||||
return;
|
|
||||||
|
|
||||||
args.StatusIcons.Add(_prototype.Index<StatusIconPrototype>(component.ZombieStatusIcon));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Content.Server.GameTicking;
|
||||||
using Content.Server.GameTicking.Rules;
|
using Content.Server.GameTicking.Rules;
|
||||||
using Content.Server.Zombies;
|
using Content.Server.Zombies;
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
@@ -8,6 +9,8 @@ using Content.Shared.Mind.Components;
|
|||||||
using Content.Shared.Verbs;
|
using Content.Shared.Verbs;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
using Content.Server.GameTicking.Rules.Components;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Content.Server.Administration.Systems;
|
namespace Content.Server.Administration.Systems;
|
||||||
|
|
||||||
@@ -17,7 +20,9 @@ public sealed partial class AdminVerbSystem
|
|||||||
[Dependency] private readonly TraitorRuleSystem _traitorRule = default!;
|
[Dependency] private readonly TraitorRuleSystem _traitorRule = default!;
|
||||||
[Dependency] private readonly NukeopsRuleSystem _nukeopsRule = default!;
|
[Dependency] private readonly NukeopsRuleSystem _nukeopsRule = default!;
|
||||||
[Dependency] private readonly PiratesRuleSystem _piratesRule = default!;
|
[Dependency] private readonly PiratesRuleSystem _piratesRule = default!;
|
||||||
|
[Dependency] private readonly RevolutionaryRuleSystem _revolutionaryRule = default!;
|
||||||
[Dependency] private readonly SharedMindSystem _minds = default!;
|
[Dependency] private readonly SharedMindSystem _minds = default!;
|
||||||
|
[Dependency] private readonly GameTicker _gameTicker = default!;
|
||||||
|
|
||||||
// All antag verbs have names so invokeverb works.
|
// All antag verbs have names so invokeverb works.
|
||||||
private void AddAntagVerbs(GetVerbsEvent<Verb> args)
|
private void AddAntagVerbs(GetVerbsEvent<Verb> args)
|
||||||
@@ -100,5 +105,22 @@ public sealed partial class AdminVerbSystem
|
|||||||
Message = Loc.GetString("admin-verb-make-pirate"),
|
Message = Loc.GetString("admin-verb-make-pirate"),
|
||||||
};
|
};
|
||||||
args.Verbs.Add(pirate);
|
args.Verbs.Add(pirate);
|
||||||
|
|
||||||
|
//todo come here at some point dear lort.
|
||||||
|
Verb headRev = new()
|
||||||
|
{
|
||||||
|
Text = Loc.GetString("admin-verb-text-make-head-rev"),
|
||||||
|
Category = VerbCategory.Antag,
|
||||||
|
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/Misc/job_icons.rsi/HeadRevolutionary.png")),
|
||||||
|
Act = () =>
|
||||||
|
{
|
||||||
|
if (!_minds.TryGetMind(args.Target, out var mindId, out var mind))
|
||||||
|
return;
|
||||||
|
_revolutionaryRule.OnHeadRevAdmin(mindId, mind);
|
||||||
|
},
|
||||||
|
Impact = LogImpact.High,
|
||||||
|
Message = Loc.GetString("admin-verb-make-head-rev"),
|
||||||
|
};
|
||||||
|
args.Verbs.Add(headRev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
237
Content.Server/Antag/AntagSelectionSystem.cs
Normal file
237
Content.Server/Antag/AntagSelectionSystem.cs
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -5,7 +5,6 @@ using Content.Server.Popups;
|
|||||||
using Content.Server.Stunnable;
|
using Content.Server.Stunnable;
|
||||||
using Content.Shared.Charges.Components;
|
using Content.Shared.Charges.Components;
|
||||||
using Content.Shared.Charges.Systems;
|
using Content.Shared.Charges.Systems;
|
||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.Eye.Blinding.Components;
|
using Content.Shared.Eye.Blinding.Components;
|
||||||
using Content.Shared.Flash;
|
using Content.Shared.Flash;
|
||||||
using Content.Shared.IdentityManagement;
|
using Content.Shared.IdentityManagement;
|
||||||
@@ -13,7 +12,6 @@ using Content.Shared.Interaction;
|
|||||||
using Content.Shared.Interaction.Events;
|
using Content.Shared.Interaction.Events;
|
||||||
using Content.Shared.Inventory;
|
using Content.Shared.Inventory;
|
||||||
using Content.Shared.Physics;
|
using Content.Shared.Physics;
|
||||||
using Content.Shared.Popups;
|
|
||||||
using Content.Shared.Tag;
|
using Content.Shared.Tag;
|
||||||
using Content.Shared.Traits.Assorted;
|
using Content.Shared.Traits.Assorted;
|
||||||
using Content.Shared.Weapons.Melee.Events;
|
using Content.Shared.Weapons.Melee.Events;
|
||||||
@@ -41,11 +39,11 @@ namespace Content.Server.Flash
|
|||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<FlashComponent, MeleeHitEvent>(OnFlashMeleeHit);
|
SubscribeLocalEvent<FlashComponent, MeleeHitEvent>(OnFlashMeleeHit);
|
||||||
// ran before toggling light for extra-bright lantern
|
// ran before toggling light for extra-bright lantern
|
||||||
SubscribeLocalEvent<FlashComponent, UseInHandEvent>(OnFlashUseInHand, before: new []{ typeof(HandheldLightSystem) });
|
SubscribeLocalEvent<FlashComponent, UseInHandEvent>(OnFlashUseInHand, before: new []{ typeof(HandheldLightSystem) });
|
||||||
SubscribeLocalEvent<InventoryComponent, FlashAttemptEvent>(OnInventoryFlashAttempt);
|
SubscribeLocalEvent<InventoryComponent, FlashAttemptEvent>(OnInventoryFlashAttempt);
|
||||||
|
|
||||||
SubscribeLocalEvent<FlashImmunityComponent, FlashAttemptEvent>(OnFlashImmunityFlashAttempt);
|
SubscribeLocalEvent<FlashImmunityComponent, FlashAttemptEvent>(OnFlashImmunityFlashAttempt);
|
||||||
SubscribeLocalEvent<PermanentBlindnessComponent, FlashAttemptEvent>(OnPermanentBlindnessFlashAttempt);
|
SubscribeLocalEvent<PermanentBlindnessComponent, FlashAttemptEvent>(OnPermanentBlindnessFlashAttempt);
|
||||||
SubscribeLocalEvent<TemporaryBlindnessComponent, FlashAttemptEvent>(OnTemporaryBlindnessFlashAttempt);
|
SubscribeLocalEvent<TemporaryBlindnessComponent, FlashAttemptEvent>(OnTemporaryBlindnessFlashAttempt);
|
||||||
@@ -63,7 +61,7 @@ namespace Content.Server.Flash
|
|||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
foreach (var e in args.HitEntities)
|
foreach (var e in args.HitEntities)
|
||||||
{
|
{
|
||||||
Flash(e, args.User, uid, comp.FlashDuration, comp.SlowTo);
|
Flash(e, args.User, uid, comp.FlashDuration, comp.SlowTo, melee: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,9 +104,17 @@ namespace Content.Server.Flash
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Flash(EntityUid target, EntityUid? user, EntityUid? used, float flashDuration, float slowTo, bool displayPopup = true, FlashableComponent? flashable = null)
|
public void Flash(EntityUid target,
|
||||||
|
EntityUid? user,
|
||||||
|
EntityUid? used,
|
||||||
|
float flashDuration,
|
||||||
|
float slowTo,
|
||||||
|
bool displayPopup = true,
|
||||||
|
FlashableComponent? flashable = null,
|
||||||
|
bool melee = false)
|
||||||
{
|
{
|
||||||
if (!Resolve(target, ref flashable, false)) return;
|
if (!Resolve(target, ref flashable, false))
|
||||||
|
return;
|
||||||
|
|
||||||
var attempt = new FlashAttemptEvent(target, user, used);
|
var attempt = new FlashAttemptEvent(target, user, used);
|
||||||
RaiseLocalEvent(target, attempt, true);
|
RaiseLocalEvent(target, attempt, true);
|
||||||
@@ -116,18 +122,28 @@ namespace Content.Server.Flash
|
|||||||
if (attempt.Cancelled)
|
if (attempt.Cancelled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (melee)
|
||||||
|
{
|
||||||
|
var ev = new AfterFlashedEvent(target, user, used);
|
||||||
|
if (user != null)
|
||||||
|
RaiseLocalEvent(user.Value, ref ev);
|
||||||
|
if (used != null)
|
||||||
|
RaiseLocalEvent(used.Value, ref ev);
|
||||||
|
}
|
||||||
|
|
||||||
flashable.LastFlash = _timing.CurTime;
|
flashable.LastFlash = _timing.CurTime;
|
||||||
flashable.Duration = flashDuration / 1000f; // TODO: Make this sane...
|
flashable.Duration = flashDuration / 1000f; // TODO: Make this sane...
|
||||||
Dirty(flashable);
|
Dirty(target, flashable);
|
||||||
|
|
||||||
_stun.TrySlowdown(target, TimeSpan.FromSeconds(flashDuration/1000f), true,
|
_stun.TrySlowdown(target, TimeSpan.FromSeconds(flashDuration/1000f), true,
|
||||||
slowTo, slowTo);
|
slowTo, slowTo);
|
||||||
|
|
||||||
if (displayPopup && user != null && target != user && EntityManager.EntityExists(user.Value))
|
if (displayPopup && user != null && target != user && Exists(user.Value))
|
||||||
{
|
{
|
||||||
user.Value.PopupMessage(target, Loc.GetString("flash-component-user-blinds-you",
|
_popup.PopupEntity(Loc.GetString("flash-component-user-blinds-you",
|
||||||
("user", Identity.Entity(user.Value, EntityManager))));
|
("user", Identity.Entity(user.Value, EntityManager))), target, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void FlashArea(EntityUid source, EntityUid? user, float range, float duration, float slowTo = 0.8f, bool displayPopup = false, SoundSpecifier? sound = null)
|
public void FlashArea(EntityUid source, EntityUid? user, float range, float duration, float slowTo = 0.8f, bool displayPopup = false, SoundSpecifier? sound = null)
|
||||||
@@ -201,4 +217,24 @@ namespace Content.Server.Flash
|
|||||||
Used = used;
|
Used = used;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Called after a flash is used via melee on another person to check for rev conversion.
|
||||||
|
/// Raised on the user of the flash, the target hit by the flash, and the flash used.
|
||||||
|
/// </summary>
|
||||||
|
[ByRefEvent]
|
||||||
|
public readonly struct AfterFlashedEvent
|
||||||
|
{
|
||||||
|
public readonly EntityUid Target;
|
||||||
|
public readonly EntityUid? User;
|
||||||
|
public readonly EntityUid? Used;
|
||||||
|
|
||||||
|
public AfterFlashedEvent(EntityUid target, EntityUid? user, EntityUid? used)
|
||||||
|
{
|
||||||
|
Target = target;
|
||||||
|
User = user;
|
||||||
|
Used = used;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
using Content.Shared.Roles;
|
||||||
|
using Robust.Shared.Audio;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||||
|
|
||||||
|
namespace Content.Server.GameTicking.Rules.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Component for the RevolutionaryRuleSystem that stores info about winning/losing, player counts required for starting, as well as prototypes for Revolutionaries and their gear.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, Access(typeof(RevolutionaryRuleSystem))]
|
||||||
|
public sealed partial class RevolutionaryRuleComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// When the round will if all the command are dead (Incase they are in space)
|
||||||
|
/// </summary>
|
||||||
|
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||||
|
public TimeSpan CommandCheck;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of time between each check for command check.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public TimeSpan TimerWait = TimeSpan.FromSeconds(20);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stores players minds
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public Dictionary<string, EntityUid> HeadRevs = new();
|
||||||
|
|
||||||
|
[DataField]
|
||||||
|
public ProtoId<AntagPrototype> RevPrototypeId = "Rev";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sound that plays when you are chosen as Rev. (Placeholder until I find something cool I guess)
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public SoundSpecifier HeadRevStartSound = new SoundPathSpecifier("/Audio/Ambience/Antag/traitor_start.ogg");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Min players needed for Revolutionary gamemode to start.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public int MinPlayers = 15;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Max Head Revs allowed during selection.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public int MaxHeadRevs = 3;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of Head Revs that will spawn per this amount of players.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public int PlayersPerHeadRev = 15;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The gear head revolutionaries are given on spawn.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public List<EntProtoId> StartingGear = new()
|
||||||
|
{
|
||||||
|
"Flash",
|
||||||
|
"ClothingEyesGlassesSunglasses"
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The time it takes after the last head is killed for the shuttle to arrive.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public TimeSpan ShuttleCallTime = TimeSpan.FromMinutes(5);
|
||||||
|
}
|
||||||
311
Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs
Normal file
311
Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs
Normal file
@@ -0,0 +1,311 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using Content.Server.Chat.Managers;
|
||||||
|
using Content.Server.GameTicking.Rules.Components;
|
||||||
|
using Content.Server.Mind;
|
||||||
|
using Content.Server.NPC.Systems;
|
||||||
|
using Content.Server.Roles;
|
||||||
|
using Content.Shared.Humanoid;
|
||||||
|
using Content.Shared.Mobs;
|
||||||
|
using Content.Shared.Mobs.Components;
|
||||||
|
using Content.Shared.Revolutionary.Components;
|
||||||
|
using Content.Shared.Roles;
|
||||||
|
using Content.Shared.Stunnable;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
using Content.Server.Popups;
|
||||||
|
using Content.Server.Revolutionary.Components;
|
||||||
|
using Content.Shared.IdentityManagement;
|
||||||
|
using Content.Server.Flash;
|
||||||
|
using Content.Shared.Mindshield.Components;
|
||||||
|
using Content.Server.Administration.Logs;
|
||||||
|
using Content.Shared.Database;
|
||||||
|
using Content.Server.Antag;
|
||||||
|
using Content.Server.NPC.Components;
|
||||||
|
using Content.Server.RoundEnd;
|
||||||
|
using Content.Shared.Chat;
|
||||||
|
using Content.Shared.Mind;
|
||||||
|
using Content.Shared.Mobs.Systems;
|
||||||
|
using Content.Shared.Zombies;
|
||||||
|
|
||||||
|
namespace Content.Server.GameTicking.Rules;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Where all the main stuff for Revolutionaries happens (Assigning Head Revs, Command on station, and checking for the game to end.)
|
||||||
|
/// </summary>
|
||||||
|
public sealed class RevolutionaryRuleSystem : GameRuleSystem<RevolutionaryRuleComponent>
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IAdminLogManager _adminLogManager = default!;
|
||||||
|
[Dependency] private readonly IChatManager _chatManager = default!;
|
||||||
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
|
[Dependency] private readonly AntagSelectionSystem _antagSelection = default!;
|
||||||
|
[Dependency] private readonly MindSystem _mind = default!;
|
||||||
|
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||||
|
[Dependency] private readonly NpcFactionSystem _npcFaction = default!;
|
||||||
|
[Dependency] private readonly PopupSystem _popup = default!;
|
||||||
|
[Dependency] private readonly RoleSystem _role = default!;
|
||||||
|
[Dependency] private readonly SharedStunSystem _stun = default!;
|
||||||
|
[Dependency] private readonly RoundEndSystem _roundEnd = default!;
|
||||||
|
|
||||||
|
[ValidatePrototypeId<NpcFactionPrototype>]
|
||||||
|
public const string RevolutionaryNpcFaction = "Revolutionary";
|
||||||
|
[ValidatePrototypeId<AntagPrototype>]
|
||||||
|
public const string RevolutionaryAntagRole = "Rev";
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
SubscribeLocalEvent<RoundStartAttemptEvent>(OnStartAttempt);
|
||||||
|
SubscribeLocalEvent<RulePlayerJobsAssignedEvent>(OnPlayerJobAssigned);
|
||||||
|
SubscribeLocalEvent<CommandStaffComponent, MobStateChangedEvent>(OnCommandMobStateChanged);
|
||||||
|
SubscribeLocalEvent<HeadRevolutionaryComponent, MobStateChangedEvent>(OnHeadRevMobStateChanged);
|
||||||
|
SubscribeLocalEvent<RoundEndTextAppendEvent>(OnRoundEndText);
|
||||||
|
SubscribeLocalEvent<HeadRevolutionaryComponent, AfterFlashedEvent>(OnPostFlash);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Started(EntityUid uid, RevolutionaryRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
|
||||||
|
{
|
||||||
|
base.Started(uid, component, gameRule, args);
|
||||||
|
component.CommandCheck = _timing.CurTime + component.TimerWait;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the round should end and also checks who has a mindshield.
|
||||||
|
/// </summary>
|
||||||
|
protected override void ActiveTick(EntityUid uid, RevolutionaryRuleComponent component, GameRuleComponent gameRule, float frameTime)
|
||||||
|
{
|
||||||
|
base.ActiveTick(uid, component, gameRule, frameTime);
|
||||||
|
if (component.CommandCheck <= _timing.CurTime)
|
||||||
|
{
|
||||||
|
component.CommandCheck = _timing.CurTime + component.TimerWait;
|
||||||
|
|
||||||
|
if (CheckCommandLose())
|
||||||
|
{
|
||||||
|
_roundEnd.DoRoundEndBehavior(RoundEndBehavior.ShuttleCall, component.ShuttleCallTime);
|
||||||
|
GameTicker.EndGameRule(uid, gameRule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRoundEndText(RoundEndTextAppendEvent ev)
|
||||||
|
{
|
||||||
|
var revsLost = CheckRevsLose();
|
||||||
|
var commandLost = CheckCommandLose();
|
||||||
|
var query = AllEntityQuery<RevolutionaryRuleComponent>();
|
||||||
|
while (query.MoveNext(out var headrev))
|
||||||
|
{
|
||||||
|
// This is (revsLost, commandsLost) concatted together
|
||||||
|
// (moony wrote this comment idk what it means)
|
||||||
|
var index = (commandLost ? 1 : 0) | (revsLost ? 2 : 0);
|
||||||
|
ev.AddLine(Loc.GetString(Outcomes[index]));
|
||||||
|
|
||||||
|
ev.AddLine(Loc.GetString("head-rev-initial-count", ("initialCount", headrev.HeadRevs.Count)));
|
||||||
|
foreach (var player in headrev.HeadRevs)
|
||||||
|
{
|
||||||
|
_mind.TryGetSession(player.Value, out var session);
|
||||||
|
var username = session?.Name;
|
||||||
|
if (username != null)
|
||||||
|
{
|
||||||
|
ev.AddLine(Loc.GetString("head-rev-initial",
|
||||||
|
("name", player.Key),
|
||||||
|
("username", username)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ev.AddLine(Loc.GetString("head-rev-initial",
|
||||||
|
("name", player.Key)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStartAttempt(RoundStartAttemptEvent ev)
|
||||||
|
{
|
||||||
|
var query = AllEntityQuery<RevolutionaryRuleComponent, GameRuleComponent>();
|
||||||
|
while (query.MoveNext(out var uid, out var comp, out var gameRule))
|
||||||
|
{
|
||||||
|
_antagSelection.AttemptStartGameRule(ev, uid, comp.MinPlayers, gameRule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPlayerJobAssigned(RulePlayerJobsAssignedEvent ev)
|
||||||
|
{
|
||||||
|
var query = QueryActiveRules();
|
||||||
|
while (query.MoveNext(out _, out var comp, out _))
|
||||||
|
{
|
||||||
|
_antagSelection.EligiblePlayers(comp.RevPrototypeId, comp.MaxHeadRevs, comp.PlayersPerHeadRev, comp.HeadRevStartSound,
|
||||||
|
"head-rev-role-greeting", "#5e9cff", out var chosen);
|
||||||
|
GiveHeadRev(chosen, comp.RevPrototypeId, comp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GiveHeadRev(List<EntityUid> chosen, string antagProto, RevolutionaryRuleComponent comp)
|
||||||
|
{
|
||||||
|
foreach (var headRev in chosen)
|
||||||
|
{
|
||||||
|
RemComp<CommandStaffComponent>(headRev);
|
||||||
|
|
||||||
|
var inCharacterName = MetaData(headRev).EntityName;
|
||||||
|
if (_mind.TryGetMind(headRev, out var mindId, out var mind))
|
||||||
|
{
|
||||||
|
if (!_role.MindHasRole<RevolutionaryRoleComponent>(mindId))
|
||||||
|
{
|
||||||
|
_role.MindAddRole(mindId, new RevolutionaryRoleComponent { PrototypeId = antagProto });
|
||||||
|
}
|
||||||
|
if (mind.Session != null)
|
||||||
|
{
|
||||||
|
comp.HeadRevs.Add(inCharacterName, mindId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_antagSelection.GiveAntagBagGear(headRev, comp.StartingGear);
|
||||||
|
EnsureComp<RevolutionaryComponent>(headRev);
|
||||||
|
EnsureComp<HeadRevolutionaryComponent>(headRev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when a Head Rev uses a flash in melee to convert somebody else.
|
||||||
|
/// </summary>
|
||||||
|
public void OnPostFlash(EntityUid uid, HeadRevolutionaryComponent comp, ref AfterFlashedEvent ev)
|
||||||
|
{
|
||||||
|
TryComp<AlwaysRevolutionaryConvertibleComponent>(ev.Target, out var alwaysConvertibleComp);
|
||||||
|
var alwaysConvertible = alwaysConvertibleComp != null;
|
||||||
|
|
||||||
|
if (!_mind.TryGetMind(ev.Target, out var mindId, out var mind) && !alwaysConvertible)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (HasComp<RevolutionaryComponent>(ev.Target) ||
|
||||||
|
HasComp<MindShieldComponent>(ev.Target) ||
|
||||||
|
!HasComp<HumanoidAppearanceComponent>(ev.Target) &&
|
||||||
|
!alwaysConvertible ||
|
||||||
|
!_mobState.IsAlive(ev.Target) ||
|
||||||
|
HasComp<ZombieComponent>(ev.Target))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_npcFaction.AddFaction(ev.Target, RevolutionaryNpcFaction);
|
||||||
|
EnsureComp<RevolutionaryComponent>(ev.Target);
|
||||||
|
_stun.TryParalyze(ev.Target, comp.StunTime, true);
|
||||||
|
if (ev.User != null)
|
||||||
|
{
|
||||||
|
_adminLogManager.Add(LogType.Mind, LogImpact.Medium, $"{ToPrettyString(ev.User.Value)} converted {ToPrettyString(ev.Target)} into a Revolutionary");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mindId == default || !_role.MindHasRole<RevolutionaryRoleComponent>(mindId))
|
||||||
|
{
|
||||||
|
_role.MindAddRole(mindId, new RevolutionaryRoleComponent { PrototypeId = RevolutionaryAntagRole });
|
||||||
|
}
|
||||||
|
if (mind?.Session != null)
|
||||||
|
{
|
||||||
|
var message = Loc.GetString("rev-role-greeting");
|
||||||
|
var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", message));
|
||||||
|
_chatManager.ChatMessageToOne(ChatChannel.Server, message, wrappedMessage, default, false, mind.Session.ConnectedClient, Color.Red);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnHeadRevAdmin(EntityUid mindId, MindComponent? mind = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(mindId, ref mind))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var revRule = EntityQuery<RevolutionaryRuleComponent>().FirstOrDefault();
|
||||||
|
if (revRule == null)
|
||||||
|
{
|
||||||
|
GameTicker.StartGameRule("Revolutionary", out var ruleEnt);
|
||||||
|
revRule = Comp<RevolutionaryRuleComponent>(ruleEnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!HasComp<HeadRevolutionaryComponent>(mind.OwnedEntity))
|
||||||
|
{
|
||||||
|
if (mind.OwnedEntity != null)
|
||||||
|
{
|
||||||
|
var player = new List<EntityUid>
|
||||||
|
{
|
||||||
|
mind.OwnedEntity.Value
|
||||||
|
};
|
||||||
|
GiveHeadRev(player, RevolutionaryAntagRole, revRule);
|
||||||
|
}
|
||||||
|
if (mind.Session != null)
|
||||||
|
{
|
||||||
|
var message = Loc.GetString("head-rev-role-greeting");
|
||||||
|
var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", message));
|
||||||
|
_chatManager.ChatMessageToOne(ChatChannel.Server, message, wrappedMessage, default, false, mind.Session.ConnectedClient, Color.FromHex("#5e9cff"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void OnCommandMobStateChanged(EntityUid uid, CommandStaffComponent comp, MobStateChangedEvent ev)
|
||||||
|
{
|
||||||
|
if (ev.NewMobState == MobState.Dead || ev.NewMobState == MobState.Invalid)
|
||||||
|
CheckCommandLose();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if all of command is dead and if so will remove all sec and command jobs if there were any left.
|
||||||
|
/// </summary>
|
||||||
|
private bool CheckCommandLose()
|
||||||
|
{
|
||||||
|
var commandList = new List<EntityUid>();
|
||||||
|
|
||||||
|
var heads = AllEntityQuery<CommandStaffComponent>();
|
||||||
|
while (heads.MoveNext(out var id, out _))
|
||||||
|
{
|
||||||
|
commandList.Add(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _antagSelection.IsGroupDead(commandList, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnHeadRevMobStateChanged(EntityUid uid, HeadRevolutionaryComponent comp, MobStateChangedEvent ev)
|
||||||
|
{
|
||||||
|
if (ev.NewMobState == MobState.Dead || ev.NewMobState == MobState.Invalid)
|
||||||
|
CheckRevsLose();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if all the Head Revs are dead and if so will deconvert all regular revs.
|
||||||
|
/// </summary>
|
||||||
|
private bool CheckRevsLose()
|
||||||
|
{
|
||||||
|
var stunTime = TimeSpan.FromSeconds(4);
|
||||||
|
var headRevList = new List<EntityUid>();
|
||||||
|
|
||||||
|
var headRevs = AllEntityQuery<HeadRevolutionaryComponent, MobStateComponent>();
|
||||||
|
while (headRevs.MoveNext(out var uid, out _, out _))
|
||||||
|
{
|
||||||
|
headRevList.Add(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no Head Revs are alive all normal Revs will lose their Rev status and rejoin Nanotrasen
|
||||||
|
if (_antagSelection.IsGroupDead(headRevList, false))
|
||||||
|
{
|
||||||
|
var rev = AllEntityQuery<RevolutionaryComponent>();
|
||||||
|
while (rev.MoveNext(out var uid, out _))
|
||||||
|
{
|
||||||
|
if (!HasComp<HeadRevolutionaryComponent>(uid))
|
||||||
|
{
|
||||||
|
_npcFaction.RemoveFaction(uid, RevolutionaryNpcFaction);
|
||||||
|
_stun.TryParalyze(uid, stunTime, true);
|
||||||
|
RemCompDeferred<RevolutionaryComponent>(uid);
|
||||||
|
_popup.PopupEntity(Loc.GetString("rev-break-control", ("name", Identity.Entity(uid, EntityManager))), uid);
|
||||||
|
_adminLogManager.Add(LogType.Mind, LogImpact.Medium, $"{ToPrettyString(uid)} was deconverted due to all Head Revolutionaries dying.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly string[] Outcomes =
|
||||||
|
{
|
||||||
|
// revs survived and heads survived... how
|
||||||
|
"rev-reverse-stalemate",
|
||||||
|
// revs won and heads died
|
||||||
|
"rev-won",
|
||||||
|
// revs lost and heads survived
|
||||||
|
"rev-lost",
|
||||||
|
// revs lost and heads died
|
||||||
|
"rev-stalemate"
|
||||||
|
};
|
||||||
|
}
|
||||||
63
Content.Server/Mindshield/MindShieldSystem.cs
Normal file
63
Content.Server/Mindshield/MindShieldSystem.cs
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
using Content.Shared.Mindshield.Components;
|
||||||
|
using Content.Shared.Revolutionary.Components;
|
||||||
|
using Content.Server.Popups;
|
||||||
|
using Content.Shared.Database;
|
||||||
|
using Content.Server.Administration.Logs;
|
||||||
|
using Content.Server.Mind;
|
||||||
|
using Content.Shared.Implants;
|
||||||
|
using Content.Shared.Tag;
|
||||||
|
using Content.Server.Roles;
|
||||||
|
using Content.Shared.Implants.Components;
|
||||||
|
|
||||||
|
namespace Content.Server.Mindshield;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// System used for checking if the implanted is a Rev or Head Rev.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class MindShieldSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IAdminLogManager _adminLogManager = default!;
|
||||||
|
[Dependency] private readonly RoleSystem _roleSystem = default!;
|
||||||
|
[Dependency] private readonly MindSystem _mindSystem = default!;
|
||||||
|
[Dependency] private readonly TagSystem _tag = default!;
|
||||||
|
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||||
|
|
||||||
|
[ValidatePrototypeId<TagPrototype>]
|
||||||
|
public const string MindShieldTag = "MindShield";
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
SubscribeLocalEvent<SubdermalImplantComponent, ImplantImplantedEvent>(ImplantCheck);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the implant was a mindshield or not
|
||||||
|
/// </summary>
|
||||||
|
public void ImplantCheck(EntityUid uid, SubdermalImplantComponent comp, ref ImplantImplantedEvent ev)
|
||||||
|
{
|
||||||
|
if (_tag.HasTag(ev.Implant, MindShieldTag) && ev.Implanted != null)
|
||||||
|
{
|
||||||
|
EnsureComp<MindShieldComponent>(ev.Implanted.Value);
|
||||||
|
MindShieldRemovalCheck(ev.Implanted, ev.Implant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the implanted person was a Rev or Head Rev and remove role or destroy mindshield respectively.
|
||||||
|
/// </summary>
|
||||||
|
public void MindShieldRemovalCheck(EntityUid? implanted, EntityUid implant)
|
||||||
|
{
|
||||||
|
if (HasComp<RevolutionaryComponent>(implanted) && !HasComp<HeadRevolutionaryComponent>(implanted))
|
||||||
|
{
|
||||||
|
_mindSystem.TryGetMind(implanted.Value, out var mindId, out _);
|
||||||
|
_adminLogManager.Add(LogType.Mind, LogImpact.Medium, $"{ToPrettyString(implanted.Value)} was deconverted due to being implanted with a Mindshield.");
|
||||||
|
_roleSystem.MindTryRemoveRole<RevolutionaryRoleComponent>(mindId);
|
||||||
|
}
|
||||||
|
else if (HasComp<RevolutionaryComponent>(implanted))
|
||||||
|
{
|
||||||
|
_popupSystem.PopupEntity(Loc.GetString("head-rev-break-mindshield"), implanted.Value);
|
||||||
|
QueueDel(implant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using Content.Server.GameTicking.Rules;
|
||||||
|
|
||||||
|
namespace Content.Server.Revolutionary.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Given to heads at round start for Revs. Used for tracking if heads died or not.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, Access(typeof(RevolutionaryRuleSystem))]
|
||||||
|
public sealed partial class CommandStaffComponent : Component
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
12
Content.Server/Roles/RevolutionaryRoleComponent.cs
Normal file
12
Content.Server/Roles/RevolutionaryRoleComponent.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using Content.Shared.Roles;
|
||||||
|
|
||||||
|
namespace Content.Server.Roles;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Added to mind entities to tag that they are a Revolutionary.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent]
|
||||||
|
public sealed partial class RevolutionaryRoleComponent : AntagonistRoleComponent
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -224,7 +224,19 @@ namespace Content.Server.RoundEnd
|
|||||||
Timer.Spawn(countdownTime.Value, AfterEndRoundRestart, _countdownTokenSource.Token);
|
Timer.Spawn(countdownTime.Value, AfterEndRoundRestart, _countdownTokenSource.Token);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DoRoundEndBehavior(RoundEndBehavior behavior, TimeSpan time, string sender, string textCall, string textAnnounce)
|
/// <summary>
|
||||||
|
/// Starts a behavior to end the round
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="behavior">The way in which the round will end</param>
|
||||||
|
/// <param name="time"></param>
|
||||||
|
/// <param name="sender"></param>
|
||||||
|
/// <param name="textCall"></param>
|
||||||
|
/// <param name="textAnnounce"></param>
|
||||||
|
public void DoRoundEndBehavior(RoundEndBehavior behavior,
|
||||||
|
TimeSpan time,
|
||||||
|
string sender = "comms-console-announcement-title-centcom",
|
||||||
|
string textCall = "round-end-system-shuttle-called-announcement",
|
||||||
|
string textAnnounce = "round-end-system-shuttle-already-called-announcement")
|
||||||
{
|
{
|
||||||
switch (behavior)
|
switch (behavior)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Shared.Actions;
|
using Content.Shared.Actions;
|
||||||
using Content.Shared.Implants.Components;
|
using Content.Shared.Implants.Components;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Interaction.Events;
|
using Content.Shared.Interaction.Events;
|
||||||
using Content.Shared.Mobs;
|
using Content.Shared.Mobs;
|
||||||
using Content.Shared.Tag;
|
using Content.Shared.Tag;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.Network;
|
using Robust.Shared.Network;
|
||||||
|
|
||||||
@@ -52,6 +53,9 @@ public abstract class SharedSubdermalImplantSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ev = new ImplantImplantedEvent(uid, component.ImplantedEntity.Value);
|
||||||
|
RaiseLocalEvent(uid, ref ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnRemoveAttempt(EntityUid uid, SubdermalImplantComponent component, ContainerGettingRemovedAttemptEvent args)
|
private void OnRemoveAttempt(EntityUid uid, SubdermalImplantComponent component, ContainerGettingRemovedAttemptEvent args)
|
||||||
@@ -128,7 +132,7 @@ public abstract class SharedSubdermalImplantSystem : EntitySystem
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="target">the implanted entity</param>
|
/// <param name="target">the implanted entity</param>
|
||||||
/// <param name="implant">the implant</param>
|
/// <param name="implant">the implant</param>
|
||||||
/// <param name="component">the implant component</param>
|
[PublicAPI]
|
||||||
public void ForceRemove(EntityUid target, EntityUid implant)
|
public void ForceRemove(EntityUid target, EntityUid implant)
|
||||||
{
|
{
|
||||||
if (!TryComp<ImplantedComponent>(target, out var implanted))
|
if (!TryComp<ImplantedComponent>(target, out var implanted))
|
||||||
@@ -144,6 +148,7 @@ public abstract class SharedSubdermalImplantSystem : EntitySystem
|
|||||||
/// Removes and deletes implants by force
|
/// Removes and deletes implants by force
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="target">The entity to have implants removed</param>
|
/// <param name="target">The entity to have implants removed</param>
|
||||||
|
[PublicAPI]
|
||||||
public void WipeImplants(EntityUid target)
|
public void WipeImplants(EntityUid target)
|
||||||
{
|
{
|
||||||
if (!TryComp<ImplantedComponent>(target, out var implanted))
|
if (!TryComp<ImplantedComponent>(target, out var implanted))
|
||||||
@@ -180,3 +185,23 @@ public sealed class ImplantRelayEvent<T> where T : notnull
|
|||||||
Event = ev;
|
Event = ev;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event that is raised whenever someone is implanted with any given implant.
|
||||||
|
/// Raised on the the implant entity.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// implant implant implant implant
|
||||||
|
/// </remarks>
|
||||||
|
[ByRefEvent]
|
||||||
|
public readonly struct ImplantImplantedEvent
|
||||||
|
{
|
||||||
|
public readonly EntityUid Implant;
|
||||||
|
public readonly EntityUid? Implanted;
|
||||||
|
|
||||||
|
public ImplantImplantedEvent(EntityUid implant, EntityUid? implanted)
|
||||||
|
{
|
||||||
|
Implant = implant;
|
||||||
|
Implanted = implanted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
12
Content.Shared/Mindshield/Components/MindShieldComponent.cs
Normal file
12
Content.Shared/Mindshield/Components/MindShieldComponent.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using Content.Shared.Revolutionary;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Mindshield.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If a player has a Mindshield they will get this component to prevent conversion.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent, Access(typeof(SharedRevolutionarySystem))]
|
||||||
|
public sealed partial class MindShieldComponent : Component
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Revolutionary.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Component used for allowing non-humans to be converted. (Mainly monkeys)
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent, Access(typeof(SharedRevolutionarySystem))]
|
||||||
|
public sealed partial class AlwaysRevolutionaryConvertibleComponent : Component
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Content.Shared.StatusIcon;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Shared.Revolutionary.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Component used for marking a Head Rev for conversion and winning/losing.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent, Access(typeof(SharedRevolutionarySystem))]
|
||||||
|
public sealed partial class HeadRevolutionaryComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The status icon corresponding to the head revolutionary.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public ProtoId<StatusIconPrototype> HeadRevStatusIcon = "HeadRevolutionaryFaction";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How long the stun will last after the user is converted.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public TimeSpan StunTime = TimeSpan.FromSeconds(3);
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Content.Shared.StatusIcon;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Shared.Revolutionary.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used for marking regular revs as well as storing icon prototypes so you can see fellow revs.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent, Access(typeof(SharedRevolutionarySystem))]
|
||||||
|
public sealed partial class RevolutionaryComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The status icon prototype displayed for revolutionaries
|
||||||
|
/// </summary>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public ProtoId<StatusIconPrototype> RevStatusIcon = "RevolutionaryFaction";
|
||||||
|
}
|
||||||
38
Content.Shared/Revolutionary/SharedRevolutionarySystem.cs
Normal file
38
Content.Shared/Revolutionary/SharedRevolutionarySystem.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
using Content.Shared.Revolutionary.Components;
|
||||||
|
using Content.Shared.IdentityManagement;
|
||||||
|
using Content.Shared.Mindshield.Components;
|
||||||
|
using Content.Shared.Popups;
|
||||||
|
using Content.Shared.Stunnable;
|
||||||
|
|
||||||
|
namespace Content.Shared.Revolutionary;
|
||||||
|
|
||||||
|
public sealed class SharedRevolutionarySystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||||
|
[Dependency] private readonly SharedStunSystem _sharedStun = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
SubscribeLocalEvent<MindShieldComponent, ComponentInit>(MindShieldImplanted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When the mindshield is implanted in the rev it will popup saying they were deconverted. In Head Revs it will remove the mindshield component.
|
||||||
|
/// </summary>
|
||||||
|
private void MindShieldImplanted(EntityUid uid, MindShieldComponent comp, ComponentInit init)
|
||||||
|
{
|
||||||
|
if (HasComp<RevolutionaryComponent>(uid) && !HasComp<HeadRevolutionaryComponent>(uid))
|
||||||
|
{
|
||||||
|
var stunTime = TimeSpan.FromSeconds(4);
|
||||||
|
var name = Identity.Entity(uid, EntityManager);
|
||||||
|
RemComp<RevolutionaryComponent>(uid);
|
||||||
|
_sharedStun.TryParalyze(uid, stunTime, true);
|
||||||
|
_popupSystem.PopupEntity(Loc.GetString("rev-break-control", ("name", name)), uid);
|
||||||
|
}
|
||||||
|
else if (HasComp<HeadRevolutionaryComponent>(uid))
|
||||||
|
{
|
||||||
|
RemCompDeferred<MindShieldComponent>(uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,9 +2,11 @@ verb-categories-antag = Antag ctrl
|
|||||||
admin-verb-make-traitor = Make the target into a traitor.
|
admin-verb-make-traitor = Make the target into a traitor.
|
||||||
admin-verb-make-zombie = Zombifies the target immediately.
|
admin-verb-make-zombie = Zombifies the target immediately.
|
||||||
admin-verb-make-nuclear-operative = Make target a into lone Nuclear Operative.
|
admin-verb-make-nuclear-operative = Make target a into lone Nuclear Operative.
|
||||||
admin-verb-make-pirate = Make the target into a pirate. Note that this doesn't configure the game rule.
|
admin-verb-make-pirate = Make the target into a pirate. Note this doesn't configure the game rule.
|
||||||
|
admin-verb-make-head-rev = Make the target into a Head Revolutionary.
|
||||||
|
|
||||||
admin-verb-text-make-traitor = Make Traitor
|
admin-verb-text-make-traitor = Make Traitor
|
||||||
admin-verb-text-make-zombie = Make Zombie
|
admin-verb-text-make-zombie = Make Zombie
|
||||||
admin-verb-text-make-nuclear-operative = Make Nuclear Operative
|
admin-verb-text-make-nuclear-operative = Make Nuclear Operative
|
||||||
admin-verb-text-make-pirate = Make Pirate
|
admin-verb-text-make-pirate = Make Pirate
|
||||||
|
admin-verb-text-make-head-rev = Make Head Rev
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
## Rev Head
|
||||||
|
|
||||||
|
roles-antag-rev-head-name = Head Revolutionary
|
||||||
|
roles-antag-rev-head-objective = Your objective is to take over the station by converting people to your cause and kill all Command staff on station.
|
||||||
|
|
||||||
|
head-rev-role-greeting =
|
||||||
|
You are a Head Revolutionary.
|
||||||
|
You are tasked with taking over the station by any means necessary.
|
||||||
|
The Syndicate has sponsored you with a flash that converts the crew to your side.
|
||||||
|
Beware, this won't work on Security, Command, or those wearing sunglasses.
|
||||||
|
Viva la revolución!
|
||||||
|
|
||||||
|
head-rev-initial = [color=#5e9cff]{$name}[/color] ([color=gray]{$username}[/color]) was one of the Head Revolutionaries.
|
||||||
|
|
||||||
|
head-rev-initial-count = {$initialCount ->
|
||||||
|
[one] There was one Head Revolutionary:
|
||||||
|
*[other] There were {$initialCount} Head Revolutionaries:
|
||||||
|
}
|
||||||
|
|
||||||
|
head-rev-break-mindshield = The Mindshield was destroyed!
|
||||||
|
|
||||||
|
## Rev
|
||||||
|
|
||||||
|
roles-antag-rev-name = Revolutionary
|
||||||
|
roles-antag-rev-objective = Your objective is to ensure the safety and follow the orders of the Head Revolutionaries as well as killing all Command staff on station.
|
||||||
|
|
||||||
|
rev-break-control = {$name} has remembered their true allegiance!
|
||||||
|
|
||||||
|
rev-role-greeting =
|
||||||
|
You are a Revolutionary.
|
||||||
|
You are tasked with taking over the station and protecting the Head Revolutionaries.
|
||||||
|
Eliminate all of the command staff.
|
||||||
|
Viva la revolución!
|
||||||
|
|
||||||
|
## General
|
||||||
|
|
||||||
|
rev-title = Revolutionaries
|
||||||
|
rev-description = Revolutionaries are among us.
|
||||||
|
|
||||||
|
rev-not-enough-ready-players = Not enough players readied up for the game. There were {$readyPlayersCount} players readied up out of {$minimumPlayers} needed. Can't start a Revolution.
|
||||||
|
rev-no-one-ready = No players readied up! Can't start a Revolution.
|
||||||
|
|
||||||
|
rev-all-heads-dead = All the heads are dead, now finish up the rest of the crew!
|
||||||
|
|
||||||
|
rev-won = The Head Revs survived and killed all of command.
|
||||||
|
|
||||||
|
rev-lost = Command survived and killed all of the Head Revs.
|
||||||
|
|
||||||
|
rev-stalemate = All of the Head Revs died and so did all of command. We'll call it a draw.
|
||||||
|
|
||||||
|
rev-reverse-stalemate = I think the Head Revs and command forgot to fight because they are both still alive.
|
||||||
|
|
||||||
|
|
||||||
@@ -4,6 +4,9 @@ ent-MedicalSupplies = { ent-CrateMedicalSupplies }
|
|||||||
ent-MedicalChemistrySupplies = { ent-CrateChemistrySupplies }
|
ent-MedicalChemistrySupplies = { ent-CrateChemistrySupplies }
|
||||||
.desc = { ent-CrateChemistrySupplies.desc }
|
.desc = { ent-CrateChemistrySupplies.desc }
|
||||||
|
|
||||||
|
ent-MedicalMindShieldImplants = { ent-MedicalMindShieldImplants }
|
||||||
|
.desc = { ent-MedicalMindShieldImplants.desc }
|
||||||
|
|
||||||
ent-EmergencyBurnKit = { ent-CrateEmergencyBurnKit }
|
ent-EmergencyBurnKit = { ent-CrateEmergencyBurnKit }
|
||||||
.desc = { ent-CrateEmergencyBurnKit.desc }
|
.desc = { ent-CrateEmergencyBurnKit.desc }
|
||||||
|
|
||||||
@@ -29,4 +32,4 @@ ent-ChemistryS = { ent-CrateChemistryS }
|
|||||||
.desc = { ent-CrateChemistryS.desc }
|
.desc = { ent-CrateChemistryS.desc }
|
||||||
|
|
||||||
ent-ChemistryD = { ent-CrateChemistryD }
|
ent-ChemistryD = { ent-CrateChemistryD }
|
||||||
.desc = { ent-CrateChemistryD.desc }
|
.desc = { ent-CrateChemistryD.desc }
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ ent-CrateMedicalSupplies = Medical supplies crate
|
|||||||
ent-CrateChemistrySupplies = Chemistry supplies crate
|
ent-CrateChemistrySupplies = Chemistry supplies crate
|
||||||
.desc = Basic chemistry supplies.
|
.desc = Basic chemistry supplies.
|
||||||
|
|
||||||
|
ent-CrateMindShieldImplants = MindShield implant crate
|
||||||
|
.desc = Crate filled with 3 MindShield implants.
|
||||||
|
|
||||||
ent-CrateMedicalSurgery = Surgical supplies crate
|
ent-CrateMedicalSurgery = Surgical supplies crate
|
||||||
.desc = Surgical instruments.
|
.desc = Surgical instruments.
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
## RoundEndSystem
|
## RoundEndSystem
|
||||||
|
|
||||||
round-end-system-shuttle-called-announcement = An emergency shuttle has been sent. ETA: {$time} {$units}.
|
round-end-system-shuttle-called-announcement = An emergency shuttle has been sent. ETA: {$time} {$units}.
|
||||||
|
round-end-system-shuttle-already-called-announcement = An emergency shuttle has already been sent.
|
||||||
round-end-system-shuttle-auto-called-announcement = An automatic crew shift change shuttle has been sent. ETA: {$time} {$units}. Recall the shuttle to extend the shift.
|
round-end-system-shuttle-auto-called-announcement = An automatic crew shift change shuttle has been sent. ETA: {$time} {$units}. Recall the shuttle to extend the shift.
|
||||||
round-end-system-shuttle-recalled-announcement = The emergency shuttle has been recalled.
|
round-end-system-shuttle-recalled-announcement = The emergency shuttle has been recalled.
|
||||||
round-end-system-round-restart-eta-announcement = Restarting the round in {$time} {$units}...
|
round-end-system-round-restart-eta-announcement = Restarting the round in {$time} {$units}...
|
||||||
|
|||||||
@@ -98,6 +98,16 @@
|
|||||||
category: Medical
|
category: Medical
|
||||||
group: market
|
group: market
|
||||||
|
|
||||||
|
- type: cargoProduct
|
||||||
|
id: MedicalMindShieldImplants
|
||||||
|
icon:
|
||||||
|
sprite: Objects/Specific/Chemistry/syringe.rsi
|
||||||
|
state: syringe_base0
|
||||||
|
product: CrateMindShieldImplants
|
||||||
|
cost: 5000
|
||||||
|
category: Medical
|
||||||
|
group: market
|
||||||
|
|
||||||
- type: cargoProduct
|
- type: cargoProduct
|
||||||
id: ChemistryP
|
id: ChemistryP
|
||||||
icon:
|
icon:
|
||||||
|
|||||||
@@ -28,6 +28,15 @@
|
|||||||
- id: BoxBottle
|
- id: BoxBottle
|
||||||
amount: 2
|
amount: 2
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: CrateMindShieldImplants
|
||||||
|
parent: CrateMedical
|
||||||
|
components:
|
||||||
|
- type: StorageFill
|
||||||
|
contents:
|
||||||
|
- id: MindShieldImplanter
|
||||||
|
amount: 3
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: CrateMedicalSurgery
|
id: CrateMedicalSurgery
|
||||||
parent: CrateSurgery
|
parent: CrateSurgery
|
||||||
|
|||||||
@@ -876,6 +876,7 @@
|
|||||||
clumsySound:
|
clumsySound:
|
||||||
path: /Audio/Animals/monkey_scream.ogg
|
path: /Audio/Animals/monkey_scream.ogg
|
||||||
- type: IdExaminable
|
- type: IdExaminable
|
||||||
|
- type: AlwaysRevolutionaryConvertible
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
name: guidebook monkey
|
name: guidebook monkey
|
||||||
|
|||||||
@@ -206,3 +206,13 @@
|
|||||||
components:
|
components:
|
||||||
- type: Implanter
|
- type: Implanter
|
||||||
implant: DeathRattleImplant
|
implant: DeathRattleImplant
|
||||||
|
|
||||||
|
# Security and Command implanters
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: MindShieldImplanter
|
||||||
|
name: mind-shield implanter
|
||||||
|
parent: BaseImplantOnlyImplanter
|
||||||
|
components:
|
||||||
|
- type: Implanter
|
||||||
|
implant: MindShieldImplant
|
||||||
|
|||||||
@@ -256,3 +256,17 @@
|
|||||||
- Dead
|
- Dead
|
||||||
- type: Rattle
|
- type: Rattle
|
||||||
|
|
||||||
|
# Sec and Command implants
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
parent: BaseSubdermalImplant
|
||||||
|
id: MindShieldImplant
|
||||||
|
name: mind-shield implant
|
||||||
|
description: This implant will ensure loyalty to Nanotrasen and prevent mind control devices.
|
||||||
|
noSpawn: true
|
||||||
|
components:
|
||||||
|
- type: SubdermalImplant
|
||||||
|
permanent: true
|
||||||
|
- type: Tag
|
||||||
|
tags:
|
||||||
|
- MindShield
|
||||||
|
|||||||
@@ -75,6 +75,13 @@
|
|||||||
components:
|
components:
|
||||||
- type: TraitorRule
|
- type: TraitorRule
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: Revolutionary
|
||||||
|
parent: BaseGameRule
|
||||||
|
noSpawn: true
|
||||||
|
components:
|
||||||
|
- type: RevolutionaryRule
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: Sandbox
|
id: Sandbox
|
||||||
parent: BaseGameRule
|
parent: BaseGameRule
|
||||||
|
|||||||
13
Resources/Prototypes/Roles/Antags/revolutionary.yml
Normal file
13
Resources/Prototypes/Roles/Antags/revolutionary.yml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
- type: antag
|
||||||
|
id: HeadRev
|
||||||
|
name: roles-antag-rev-head-name
|
||||||
|
antagonist: true
|
||||||
|
setPreference: true
|
||||||
|
objective: roles-antag-rev-head-objective
|
||||||
|
|
||||||
|
- type: antag
|
||||||
|
id: Rev
|
||||||
|
name: roles-antag-rev-name
|
||||||
|
antagonist: true
|
||||||
|
setPreference: false
|
||||||
|
objective: roles-antag-rev-objective
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
department: Cargo
|
department: Cargo
|
||||||
time: 36000 #10 hours
|
time: 36000 #10 hours
|
||||||
- !type:OverallPlaytimeRequirement
|
- !type:OverallPlaytimeRequirement
|
||||||
time: 144000 #40 hrs
|
time: 144000 #40 hrs
|
||||||
weight: 10
|
weight: 10
|
||||||
startingGear: QuartermasterGear
|
startingGear: QuartermasterGear
|
||||||
icon: "JobIconQuarterMaster"
|
icon: "JobIconQuarterMaster"
|
||||||
@@ -27,6 +27,12 @@
|
|||||||
- Maintenance
|
- Maintenance
|
||||||
- External
|
- External
|
||||||
- Command
|
- Command
|
||||||
|
special:
|
||||||
|
- !type:AddImplantSpecial
|
||||||
|
implants: [ MindShieldImplant ]
|
||||||
|
- !type:AddComponentSpecial
|
||||||
|
components:
|
||||||
|
- type: CommandStaff
|
||||||
|
|
||||||
- type: startingGear
|
- type: startingGear
|
||||||
id: QuartermasterGear
|
id: QuartermasterGear
|
||||||
|
|||||||
@@ -25,6 +25,12 @@
|
|||||||
canBeAntag: false
|
canBeAntag: false
|
||||||
accessGroups:
|
accessGroups:
|
||||||
- AllAccess
|
- AllAccess
|
||||||
|
special:
|
||||||
|
- !type:AddImplantSpecial
|
||||||
|
implants: [ MindShieldImplant ]
|
||||||
|
- !type:AddComponentSpecial
|
||||||
|
components:
|
||||||
|
- type: CommandStaff
|
||||||
|
|
||||||
- type: startingGear
|
- type: startingGear
|
||||||
id: CaptainGear
|
id: CaptainGear
|
||||||
|
|||||||
@@ -47,6 +47,12 @@
|
|||||||
- Cargo
|
- Cargo
|
||||||
- Atmospherics
|
- Atmospherics
|
||||||
- Medical
|
- Medical
|
||||||
|
special:
|
||||||
|
- !type:AddImplantSpecial
|
||||||
|
implants: [ MindShieldImplant ]
|
||||||
|
- !type:AddComponentSpecial
|
||||||
|
components:
|
||||||
|
- type: CommandStaff
|
||||||
|
|
||||||
- type: startingGear
|
- type: startingGear
|
||||||
id: HoPGear
|
id: HoPGear
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
department: Engineering
|
department: Engineering
|
||||||
time: 36000 #10 hrs
|
time: 36000 #10 hrs
|
||||||
- !type:OverallPlaytimeRequirement
|
- !type:OverallPlaytimeRequirement
|
||||||
time: 144000 #40 hrs
|
time: 144000 #40 hrs
|
||||||
weight: 10
|
weight: 10
|
||||||
startingGear: ChiefEngineerGear
|
startingGear: ChiefEngineerGear
|
||||||
icon: "JobIconChiefEngineer"
|
icon: "JobIconChiefEngineer"
|
||||||
@@ -28,6 +28,12 @@
|
|||||||
- External
|
- External
|
||||||
- ChiefEngineer
|
- ChiefEngineer
|
||||||
- Atmospherics
|
- Atmospherics
|
||||||
|
special:
|
||||||
|
- !type:AddImplantSpecial
|
||||||
|
implants: [ MindShieldImplant ]
|
||||||
|
- !type:AddComponentSpecial
|
||||||
|
components:
|
||||||
|
- type: CommandStaff
|
||||||
|
|
||||||
- type: startingGear
|
- type: startingGear
|
||||||
id: ChiefEngineerGear
|
id: ChiefEngineerGear
|
||||||
|
|||||||
@@ -326,7 +326,13 @@
|
|||||||
satchel: ClothingBackpackSatchelBrigmedicFilled
|
satchel: ClothingBackpackSatchelBrigmedicFilled
|
||||||
duffelbag: ClothingBackpackDuffelBrigmedicFilled
|
duffelbag: ClothingBackpackDuffelBrigmedicFilled
|
||||||
|
|
||||||
#Gladiator with spear
|
#Head Rev Gear
|
||||||
|
- type: startingGear
|
||||||
|
id: HeadRevGear
|
||||||
|
equipment:
|
||||||
|
pocket2: Flash
|
||||||
|
|
||||||
|
#Gladiator with spear
|
||||||
- type: startingGear
|
- type: startingGear
|
||||||
id: GladiatorGear
|
id: GladiatorGear
|
||||||
equipment:
|
equipment:
|
||||||
@@ -335,7 +341,7 @@
|
|||||||
head: ClothingHeadHatGladiator
|
head: ClothingHeadHatGladiator
|
||||||
shoes: ClothingShoesCult
|
shoes: ClothingShoesCult
|
||||||
|
|
||||||
#Ash Walker
|
#Ash Walker
|
||||||
- type: startingGear
|
- type: startingGear
|
||||||
id: AshWalker
|
id: AshWalker
|
||||||
equipment:
|
equipment:
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
department: Medical
|
department: Medical
|
||||||
time: 36000 #10 hrs
|
time: 36000 #10 hrs
|
||||||
- !type:OverallPlaytimeRequirement
|
- !type:OverallPlaytimeRequirement
|
||||||
time: 144000 #40 hrs
|
time: 144000 #40 hrs
|
||||||
weight: 10
|
weight: 10
|
||||||
startingGear: CMOGear
|
startingGear: CMOGear
|
||||||
icon: "JobIconChiefMedicalOfficer"
|
icon: "JobIconChiefMedicalOfficer"
|
||||||
@@ -29,6 +29,12 @@
|
|||||||
- Maintenance
|
- Maintenance
|
||||||
- Chemistry
|
- Chemistry
|
||||||
- ChiefMedicalOfficer
|
- ChiefMedicalOfficer
|
||||||
|
special:
|
||||||
|
- !type:AddImplantSpecial
|
||||||
|
implants: [ MindShieldImplant ]
|
||||||
|
- !type:AddComponentSpecial
|
||||||
|
components:
|
||||||
|
- type: CommandStaff
|
||||||
|
|
||||||
- type: startingGear
|
- type: startingGear
|
||||||
id: CMOGear
|
id: CMOGear
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
department: Science
|
department: Science
|
||||||
time: 36000 #10 hrs
|
time: 36000 #10 hrs
|
||||||
- !type:OverallPlaytimeRequirement
|
- !type:OverallPlaytimeRequirement
|
||||||
time: 144000 #40 hrs
|
time: 144000 #40 hrs
|
||||||
weight: 10
|
weight: 10
|
||||||
startingGear: ResearchDirectorGear
|
startingGear: ResearchDirectorGear
|
||||||
icon: "JobIconResearchDirector"
|
icon: "JobIconResearchDirector"
|
||||||
@@ -20,6 +20,12 @@
|
|||||||
- Command
|
- Command
|
||||||
- Maintenance
|
- Maintenance
|
||||||
- ResearchDirector
|
- ResearchDirector
|
||||||
|
special:
|
||||||
|
- !type:AddImplantSpecial
|
||||||
|
implants: [ MindShieldImplant ]
|
||||||
|
- !type:AddComponentSpecial
|
||||||
|
components:
|
||||||
|
- type: CommandStaff
|
||||||
|
|
||||||
- type: startingGear
|
- type: startingGear
|
||||||
id: ResearchDirectorGear
|
id: ResearchDirectorGear
|
||||||
|
|||||||
@@ -17,6 +17,9 @@
|
|||||||
- Maintenance
|
- Maintenance
|
||||||
- Service
|
- Service
|
||||||
- Detective
|
- Detective
|
||||||
|
special:
|
||||||
|
- !type:AddImplantSpecial
|
||||||
|
implants: [ MindShieldImplant ]
|
||||||
|
|
||||||
- type: startingGear
|
- type: startingGear
|
||||||
id: DetectiveGear
|
id: DetectiveGear
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
department: Security
|
department: Security
|
||||||
time: 108000 # 30 hrs
|
time: 108000 # 30 hrs
|
||||||
- !type:OverallPlaytimeRequirement
|
- !type:OverallPlaytimeRequirement
|
||||||
time: 144000 #40 hrs
|
time: 144000 #40 hrs
|
||||||
weight: 10
|
weight: 10
|
||||||
startingGear: HoSGear
|
startingGear: HoSGear
|
||||||
icon: "JobIconHeadOfSecurity"
|
icon: "JobIconHeadOfSecurity"
|
||||||
@@ -31,6 +31,12 @@
|
|||||||
- Service
|
- Service
|
||||||
- External
|
- External
|
||||||
- Detective
|
- Detective
|
||||||
|
special:
|
||||||
|
- !type:AddImplantSpecial
|
||||||
|
implants: [ MindShieldImplant ]
|
||||||
|
- !type:AddComponentSpecial
|
||||||
|
components:
|
||||||
|
- type: CommandStaff
|
||||||
|
|
||||||
- type: startingGear
|
- type: startingGear
|
||||||
id: HoSGear
|
id: HoSGear
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
- type: job
|
- type: job
|
||||||
id: SecurityCadet
|
id: SecurityCadet
|
||||||
name: job-name-cadet
|
name: job-name-cadet
|
||||||
description: job-description-cadet
|
description: job-description-cadet
|
||||||
@@ -18,6 +18,9 @@
|
|||||||
- Security
|
- Security
|
||||||
- Brig
|
- Brig
|
||||||
- Maintenance
|
- Maintenance
|
||||||
|
special:
|
||||||
|
- !type:AddImplantSpecial
|
||||||
|
implants: [ MindShieldImplant ]
|
||||||
|
|
||||||
- type: startingGear
|
- type: startingGear
|
||||||
id: SecurityCadetGear
|
id: SecurityCadetGear
|
||||||
|
|||||||
@@ -17,6 +17,9 @@
|
|||||||
- Maintenance
|
- Maintenance
|
||||||
- Service
|
- Service
|
||||||
- External
|
- External
|
||||||
|
special:
|
||||||
|
- !type:AddImplantSpecial
|
||||||
|
implants: [ MindShieldImplant ]
|
||||||
|
|
||||||
- type: startingGear
|
- type: startingGear
|
||||||
id: SecurityOfficerGear
|
id: SecurityOfficerGear
|
||||||
|
|||||||
@@ -26,6 +26,9 @@
|
|||||||
- Maintenance
|
- Maintenance
|
||||||
- Service
|
- Service
|
||||||
- External
|
- External
|
||||||
|
special:
|
||||||
|
- !type:AddImplantSpecial
|
||||||
|
implants: [ MindShieldImplant ]
|
||||||
|
|
||||||
- type: startingGear
|
- type: startingGear
|
||||||
id: SeniorOfficerGear
|
id: SeniorOfficerGear
|
||||||
|
|||||||
@@ -19,6 +19,9 @@
|
|||||||
- Brig
|
- Brig
|
||||||
- External
|
- External
|
||||||
- Detective
|
- Detective
|
||||||
|
special:
|
||||||
|
- !type:AddImplantSpecial
|
||||||
|
implants: [ MindShieldImplant ]
|
||||||
|
|
||||||
- type: startingGear
|
- type: startingGear
|
||||||
id: WardenGear
|
id: WardenGear
|
||||||
|
|||||||
@@ -1,6 +1,20 @@
|
|||||||
- type: statusIcon
|
- type: statusIcon
|
||||||
id: ZombieFaction
|
id: ZombieFaction
|
||||||
priority: 11
|
priority: 11
|
||||||
icon:
|
icon:
|
||||||
sprite: Interface/Misc/job_icons.rsi
|
sprite: Interface/Misc/job_icons.rsi
|
||||||
state: Zombie
|
state: Zombie
|
||||||
|
|
||||||
|
- type: statusIcon
|
||||||
|
id: RevolutionaryFaction
|
||||||
|
priority: 11
|
||||||
|
icon:
|
||||||
|
sprite: Interface/Misc/job_icons.rsi
|
||||||
|
state: Revolutionary
|
||||||
|
|
||||||
|
- type: statusIcon
|
||||||
|
id: HeadRevolutionaryFaction
|
||||||
|
priority: 11
|
||||||
|
icon:
|
||||||
|
sprite: Interface/Misc/job_icons.rsi
|
||||||
|
state: HeadRevolutionary
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
- Xeno
|
- Xeno
|
||||||
- PetsNT
|
- PetsNT
|
||||||
- Zombie
|
- Zombie
|
||||||
|
- Revolutionary
|
||||||
|
|
||||||
- type: npcFaction
|
- type: npcFaction
|
||||||
id: NanoTrasen
|
id: NanoTrasen
|
||||||
@@ -14,6 +15,7 @@
|
|||||||
- Syndicate
|
- Syndicate
|
||||||
- Xeno
|
- Xeno
|
||||||
- Zombie
|
- Zombie
|
||||||
|
- Revolutionary
|
||||||
|
|
||||||
- type: npcFaction
|
- type: npcFaction
|
||||||
id: Mouse
|
id: Mouse
|
||||||
@@ -39,6 +41,7 @@
|
|||||||
- Passive
|
- Passive
|
||||||
- PetsNT
|
- PetsNT
|
||||||
- Zombie
|
- Zombie
|
||||||
|
- Revolutionary
|
||||||
|
|
||||||
- type: npcFaction
|
- type: npcFaction
|
||||||
id: SimpleNeutral
|
id: SimpleNeutral
|
||||||
@@ -60,6 +63,7 @@
|
|||||||
- Passive
|
- Passive
|
||||||
- PetsNT
|
- PetsNT
|
||||||
- Zombie
|
- Zombie
|
||||||
|
- Revolutionary
|
||||||
|
|
||||||
- type: npcFaction
|
- type: npcFaction
|
||||||
id: Zombie
|
id: Zombie
|
||||||
@@ -70,3 +74,12 @@
|
|||||||
- Syndicate
|
- Syndicate
|
||||||
- Passive
|
- Passive
|
||||||
- PetsNT
|
- PetsNT
|
||||||
|
- Revolutionary
|
||||||
|
|
||||||
|
- type: npcFaction
|
||||||
|
id: Revolutionary
|
||||||
|
hostile:
|
||||||
|
- NanoTrasen
|
||||||
|
- Zombie
|
||||||
|
- SimpleHostile
|
||||||
|
- Dragon
|
||||||
|
|||||||
@@ -76,6 +76,19 @@
|
|||||||
- Nukeops
|
- Nukeops
|
||||||
- BasicStationEventScheduler
|
- BasicStationEventScheduler
|
||||||
|
|
||||||
|
- type: gamePreset
|
||||||
|
id: Revolutionary
|
||||||
|
alias:
|
||||||
|
- rev
|
||||||
|
- revs
|
||||||
|
- revolutionaries
|
||||||
|
name: rev-title
|
||||||
|
description: rev-description
|
||||||
|
showInVote: false
|
||||||
|
rules:
|
||||||
|
- Revolutionary
|
||||||
|
- BasicStationEventScheduler
|
||||||
|
|
||||||
- type: gamePreset
|
- type: gamePreset
|
||||||
id: Zombie
|
id: Zombie
|
||||||
alias:
|
alias:
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
- type: weightedRandom
|
- type: weightedRandom
|
||||||
id: Secret
|
id: Secret
|
||||||
weights:
|
weights:
|
||||||
Nukeops: 0.25
|
Nukeops: 0.15
|
||||||
Traitor: 0.65
|
Traitor: 0.60
|
||||||
Zombie: 0.10
|
Zombie: 0.10
|
||||||
|
Revolutionary: 0.15
|
||||||
|
|
||||||
@@ -1057,3 +1057,5 @@
|
|||||||
- type: Tag
|
- type: Tag
|
||||||
id: ModularReceiver
|
id: ModularReceiver
|
||||||
|
|
||||||
|
- type: Tag
|
||||||
|
id: MindShield
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
@@ -1,7 +1,8 @@
|
|||||||
{
|
{
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"license": "CC-BY-SA-3.0",
|
"license": "CC-BY-SA-3.0",
|
||||||
"copyright": "Taken from https://github.com/vgstation-coders/vgstation13/blob/e71d6c4fba5a51f99b81c295dcaec4fc2f58fb19/icons/mob/screen1.dmi | Brigmedic icon made by PuroSlavKing (Github) | Zombie icon made by RamZ | Zookeper by netwy (discort)",
|
"copyright": "Taken from https://github.com/vgstation-coders/vgstation13/blob/e71d6c4fba5a51f99b81c295dcaec4fc2f58fb19/icons/mob/screen1.dmi | Brigmedic icon made by PuroSlavKing (Github) | Zombie icon made by RamZ | Zookeper by netwy (discort) | Rev and Head Rev icon taken from https://tgstation13.org/wiki/HUD and edited by coolmankid12345 (Discord)",
|
||||||
|
|
||||||
"size": {
|
"size": {
|
||||||
"x": 8,
|
"x": 8,
|
||||||
"y": 8
|
"y": 8
|
||||||
@@ -162,6 +163,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "SeniorOfficer"
|
"name": "SeniorOfficer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Revolutionary"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "HeadRevolutionary"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user