Merge remote-tracking branch 'refs/remotes/wizards/master'
# Conflicts: # Content.Server/Administration/Systems/AdminSystem.cs # Content.Server/GameTicking/GameTicker.Spawning.cs # Content.Server/IoC/ServerContentIoC.cs # Resources/Locale/en-US/_strings/administration/multi-server-kick.ftl # Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Cartridges/shotgun.yml
This commit is contained in:
commit
3c2672efe9
45 changed files with 518 additions and 157 deletions
|
|
@ -1,6 +1,4 @@
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Content.Server.Database;
|
||||
|
||||
namespace Content.Server.Administration.Managers;
|
||||
|
||||
|
|
@ -30,36 +28,15 @@ public sealed partial class BanManager
|
|||
private TimeSpan _banNotificationRateLimitStart;
|
||||
private int _banNotificationRateLimitCount;
|
||||
|
||||
private void OnDatabaseNotification(DatabaseNotification notification)
|
||||
private bool OnDatabaseNotificationEarlyFilter()
|
||||
{
|
||||
if (notification.Channel != BanNotificationChannel)
|
||||
return;
|
||||
|
||||
if (notification.Payload == null)
|
||||
{
|
||||
_sawmill.Error("Got ban notification with null payload!");
|
||||
return;
|
||||
}
|
||||
|
||||
BanNotificationData data;
|
||||
try
|
||||
{
|
||||
data = JsonSerializer.Deserialize<BanNotificationData>(notification.Payload)
|
||||
?? throw new JsonException("Content is null");
|
||||
}
|
||||
catch (JsonException e)
|
||||
{
|
||||
_sawmill.Error($"Got invalid JSON in ban notification: {e}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CheckBanRateLimit())
|
||||
{
|
||||
_sawmill.Verbose("Not processing ban notification due to rate limit");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
_taskManager.RunOnMainThread(() => ProcessBanNotification(data));
|
||||
return true;
|
||||
}
|
||||
|
||||
private async void ProcessBanNotification(BanNotificationData data)
|
||||
|
|
|
|||
|
|
@ -75,7 +75,12 @@ public sealed partial class BanManager : IBanManager, IPostInjectInit
|
|||
{
|
||||
_netManager.RegisterNetMessage<MsgRoleBans>();
|
||||
|
||||
_db.SubscribeToNotifications(OnDatabaseNotification);
|
||||
_db.SubscribeToJsonNotification<BanNotificationData>(
|
||||
_taskManager,
|
||||
_sawmill,
|
||||
BanNotificationChannel,
|
||||
ProcessBanNotification,
|
||||
OnDatabaseNotificationEarlyFilter);
|
||||
|
||||
_userDbData.AddOnLoadPlayer(CachePlayerData);
|
||||
_userDbData.AddOnPlayerDisconnect(ClearPlayerData);
|
||||
|
|
|
|||
114
Content.Server/Administration/Managers/MultiServerKickManager.cs
Normal file
114
Content.Server/Administration/Managers/MultiServerKickManager.cs
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Content.Server.Database;
|
||||
using Content.Shared.CCVar;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Asynchronous;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Server.Administration.Managers;
|
||||
|
||||
/// <summary>
|
||||
/// Handles kicking people that connect to multiple servers on the same DB at once.
|
||||
/// </summary>
|
||||
/// <seealso cref="CCVars.AdminAllowMultiServerPlay"/>
|
||||
public sealed class MultiServerKickManager
|
||||
{
|
||||
public const string NotificationChannel = "multi_server_kick";
|
||||
|
||||
[Dependency] private readonly IPlayerManager _playerManager = null!;
|
||||
[Dependency] private readonly IServerDbManager _dbManager = null!;
|
||||
[Dependency] private readonly ILogManager _logManager = null!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = null!;
|
||||
[Dependency] private readonly IAdminManager _adminManager = null!;
|
||||
[Dependency] private readonly ITaskManager _taskManager = null!;
|
||||
[Dependency] private readonly IServerNetManager _netManager = null!;
|
||||
[Dependency] private readonly ILocalizationManager _loc = null!;
|
||||
[Dependency] private readonly ServerDbEntryManager _serverDbEntry = null!;
|
||||
|
||||
private ISawmill _sawmill = null!;
|
||||
private bool _allowed;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_sawmill = _logManager.GetSawmill("multi_server_kick");
|
||||
|
||||
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
|
||||
_cfg.OnValueChanged(CCVars.AdminAllowMultiServerPlay, b => _allowed = b, true);
|
||||
|
||||
_dbManager.SubscribeToJsonNotification<NotificationData>(
|
||||
_taskManager,
|
||||
_sawmill,
|
||||
NotificationChannel,
|
||||
OnNotification,
|
||||
OnNotificationEarlyFilter
|
||||
);
|
||||
}
|
||||
|
||||
// ReSharper disable once AsyncVoidMethod
|
||||
private async void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e)
|
||||
{
|
||||
if (_allowed)
|
||||
return;
|
||||
|
||||
if (e.NewStatus != SessionStatus.InGame)
|
||||
return;
|
||||
|
||||
// Send notification to other servers so they can kick this player that just connected.
|
||||
try
|
||||
{
|
||||
await _dbManager.SendNotification(new DatabaseNotification
|
||||
{
|
||||
Channel = NotificationChannel,
|
||||
Payload = JsonSerializer.Serialize(new NotificationData
|
||||
{
|
||||
PlayerId = e.Session.UserId,
|
||||
ServerId = (await _serverDbEntry.ServerEntity).Id,
|
||||
}),
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_sawmill.Error($"Failed to send notification for multi server kick: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
private bool OnNotificationEarlyFilter()
|
||||
{
|
||||
if (_allowed)
|
||||
{
|
||||
_sawmill.Verbose("Received notification for player join, but multi server play is allowed on this server. Ignoring");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ReSharper disable once AsyncVoidMethod
|
||||
private async void OnNotification(NotificationData notification)
|
||||
{
|
||||
if (!_playerManager.TryGetSessionById(new NetUserId(notification.PlayerId), out var player))
|
||||
return;
|
||||
|
||||
if (notification.ServerId == (await _serverDbEntry.ServerEntity).Id)
|
||||
return;
|
||||
|
||||
if (_adminManager.IsAdmin(player, includeDeAdmin: true))
|
||||
return;
|
||||
|
||||
_sawmill.Info($"Kicking {player} for connecting to another server. Multi-server play is not allowed.");
|
||||
_netManager.DisconnectChannel(player.Channel, _loc.GetString("multi-server-kick-reason"));
|
||||
}
|
||||
|
||||
private sealed class NotificationData
|
||||
{
|
||||
[JsonPropertyName("player_id")]
|
||||
public Guid PlayerId { get; set; }
|
||||
|
||||
[JsonPropertyName("server_id")]
|
||||
public int ServerId { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -85,13 +85,14 @@ public sealed class AdminSystem : EntitySystem
|
|||
Subs.CVar(_config, CCVars.PanicBunkerMinAccountAge, OnPanicBunkerMinAccountAgeChanged, true);
|
||||
Subs.CVar(_config, CCVars.PanicBunkerMinOverallMinutes, OnPanicBunkerMinOverallMinutesChanged, true);
|
||||
|
||||
SubscribeLocalEvent<IdentityChangedEvent>(OnIdentityChanged);
|
||||
SubscribeLocalEvent<PlayerAttachedEvent>(OnPlayerAttached);
|
||||
SubscribeLocalEvent<PlayerDetachedEvent>(OnPlayerDetached);
|
||||
SubscribeLocalEvent<RoleAddedEvent>(OnRoleEvent);
|
||||
SubscribeLocalEvent<RoleRemovedEvent>(OnRoleEvent);
|
||||
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundRestartCleanup);
|
||||
|
||||
SubscribeLocalEvent<ActorComponent, EntityRenamedEvent>(OnPlayerRenamed);
|
||||
SubscribeLocalEvent<ActorComponent, IdentityChangedEvent>(OnIdentityChanged);
|
||||
|
||||
IoCManager.Instance!.TryResolveType(out _sponsorsManager); // Sunrise-Sponsors
|
||||
}
|
||||
|
|
@ -149,12 +150,9 @@ public sealed class AdminSystem : EntitySystem
|
|||
return value ?? null;
|
||||
}
|
||||
|
||||
private void OnIdentityChanged(ref IdentityChangedEvent ev)
|
||||
private void OnIdentityChanged(Entity<ActorComponent> ent, ref IdentityChangedEvent ev)
|
||||
{
|
||||
if (!TryComp<ActorComponent>(ev.CharacterEntity, out var actor))
|
||||
return;
|
||||
|
||||
UpdatePlayerList(actor.PlayerSession);
|
||||
UpdatePlayerList(ent.Comp.PlayerSession);
|
||||
}
|
||||
|
||||
private void OnRoleEvent(RoleEvent ev)
|
||||
|
|
|
|||
|
|
@ -1809,6 +1809,8 @@ INSERT INTO player_round (players_id, rounds_id) VALUES ({players[player]}, {id}
|
|||
|
||||
#endregion
|
||||
|
||||
public abstract Task SendNotification(DatabaseNotification notification);
|
||||
|
||||
// SQLite returns DateTime as Kind=Unspecified, Npgsql actually knows for sure it's Kind=Utc.
|
||||
// Normalize DateTimes here so they're always Utc. Thanks.
|
||||
protected abstract DateTime NormalizeDatabaseTime(DateTime time);
|
||||
|
|
|
|||
|
|
@ -350,6 +350,15 @@ namespace Content.Server.Database
|
|||
/// <param name="notification">The notification to trigger</param>
|
||||
void InjectTestNotification(DatabaseNotification notification);
|
||||
|
||||
/// <summary>
|
||||
/// Send a notification to all other servers connected to the same database.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The local server will receive the sent notification itself again.
|
||||
/// </remarks>
|
||||
/// <param name="notification">The notification to send.</param>
|
||||
Task SendNotification(DatabaseNotification notification);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
|
@ -1045,6 +1054,12 @@ namespace Content.Server.Database
|
|||
HandleDatabaseNotification(notification);
|
||||
}
|
||||
|
||||
public Task SendNotification(DatabaseNotification notification)
|
||||
{
|
||||
DbWriteOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.SendNotification(notification));
|
||||
}
|
||||
|
||||
private async void HandleDatabaseNotification(DatabaseNotification notification)
|
||||
{
|
||||
lock (_notificationHandlers)
|
||||
|
|
|
|||
76
Content.Server/Database/ServerDbManagerExt.cs
Normal file
76
Content.Server/Database/ServerDbManagerExt.cs
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
using System.Text.Json;
|
||||
using Robust.Shared.Asynchronous;
|
||||
|
||||
namespace Content.Server.Database;
|
||||
|
||||
public static class ServerDbManagerExt
|
||||
{
|
||||
/// <summary>
|
||||
/// Subscribe to a database notification on a specific channel, formatted as JSON.
|
||||
/// </summary>
|
||||
/// <param name="dbManager">The database manager to subscribe on.</param>
|
||||
/// <param name="taskManager">The task manager used to run the main callback on the main thread.</param>
|
||||
/// <param name="sawmill">Sawmill to log any errors to.</param>
|
||||
/// <param name="channel">
|
||||
/// The notification channel to listen on. Only notifications on this channel will be handled.
|
||||
/// </param>
|
||||
/// <param name="action">
|
||||
/// The action to run on the notification data.
|
||||
/// This runs on the main thread.
|
||||
/// </param>
|
||||
/// <param name="earlyFilter">
|
||||
/// An early filter callback that runs before the JSON message is deserialized.
|
||||
/// Return false to not handle the notification.
|
||||
/// This does not run on the main thread.
|
||||
/// </param>
|
||||
/// <param name="filter">
|
||||
/// A filter callback that runs after the JSON message is deserialized.
|
||||
/// Return false to not handle the notification.
|
||||
/// This does not run on the main thread.
|
||||
/// </param>
|
||||
/// <typeparam name="TData">The type of JSON data to deserialize.</typeparam>
|
||||
public static void SubscribeToJsonNotification<TData>(
|
||||
this IServerDbManager dbManager,
|
||||
ITaskManager taskManager,
|
||||
ISawmill sawmill,
|
||||
string channel,
|
||||
Action<TData> action,
|
||||
Func<bool>? earlyFilter = null,
|
||||
Func<TData, bool>? filter = null)
|
||||
{
|
||||
dbManager.SubscribeToNotifications(notification =>
|
||||
{
|
||||
if (notification.Channel != channel)
|
||||
return;
|
||||
|
||||
if (notification.Payload == null)
|
||||
{
|
||||
sawmill.Error($"Got {channel} notification with null payload!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (earlyFilter != null && !earlyFilter())
|
||||
return;
|
||||
|
||||
TData data;
|
||||
try
|
||||
{
|
||||
data = JsonSerializer.Deserialize<TData>(notification.Payload)
|
||||
?? throw new JsonException("Content is null");
|
||||
}
|
||||
catch (JsonException e)
|
||||
{
|
||||
sawmill.Error($"Got invalid JSON in {channel} notification: {e}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (filter != null && !filter(data))
|
||||
return;
|
||||
|
||||
taskManager.RunOnMainThread(() =>
|
||||
{
|
||||
action(data);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Administration.Managers;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Npgsql;
|
||||
|
||||
namespace Content.Server.Database;
|
||||
|
|
@ -17,6 +18,7 @@ public sealed partial class ServerDbPostgres
|
|||
private static readonly string[] NotificationChannels =
|
||||
[
|
||||
BanManager.BanNotificationChannel,
|
||||
MultiServerKickManager.NotificationChannel,
|
||||
];
|
||||
|
||||
private static readonly TimeSpan ReconnectWaitIncrease = TimeSpan.FromSeconds(10);
|
||||
|
|
@ -111,6 +113,14 @@ public sealed partial class ServerDbPostgres
|
|||
});
|
||||
}
|
||||
|
||||
public override async Task SendNotification(DatabaseNotification notification)
|
||||
{
|
||||
await using var db = await GetDbImpl();
|
||||
|
||||
await db.PgDbContext.Database.ExecuteSqlAsync(
|
||||
$"SELECT pg_notify({notification.Channel}, {notification.Payload})");
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
_notificationTokenSource.Cancel();
|
||||
|
|
|
|||
|
|
@ -537,6 +537,12 @@ namespace Content.Server.Database
|
|||
return await base.AddAdminMessage(message);
|
||||
}
|
||||
|
||||
public override Task SendNotification(DatabaseNotification notification)
|
||||
{
|
||||
// Notifications not implemented on SQLite.
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
protected override DateTime NormalizeDatabaseTime(DateTime time)
|
||||
{
|
||||
DebugTools.Assert(time.Kind == DateTimeKind.Unspecified);
|
||||
|
|
|
|||
|
|
@ -171,6 +171,7 @@ namespace Content.Server.Entry
|
|||
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<GameTicker>().PostInitialize();
|
||||
IoCManager.Resolve<IBanManager>().Initialize();
|
||||
IoCManager.Resolve<IConnectionManager>().PostInit();
|
||||
IoCManager.Resolve<MultiServerKickManager>().Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using System.Linq;
|
|||
using System.Numerics;
|
||||
using Content.Server._Sunrise.Station;
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Server.Administration.Systems;
|
||||
using Content.Server.GameTicking.Events;
|
||||
using Content.Server.Ghost;
|
||||
using Content.Server.Shuttles.Components;
|
||||
|
|
@ -34,6 +35,7 @@ namespace Content.Server.GameTicking
|
|||
{
|
||||
[Dependency] private readonly IAdminManager _adminManager = default!;
|
||||
[Dependency] private readonly SharedJobSystem _jobs = default!;
|
||||
[Dependency] private readonly AdminSystem _admin = default!;
|
||||
[Dependency] private readonly PlayTimeTrackingManager _playTimeTracking = default!;
|
||||
[Dependency] private readonly NewLifeSystem _newLifeSystem = default!; // Sunrise-NewLife
|
||||
|
||||
|
|
@ -261,6 +263,7 @@ namespace Content.Server.GameTicking
|
|||
|
||||
_roles.MindAddJobRole(newMind, silent: silent, jobPrototype:jobId);
|
||||
var jobName = _jobs.MindTryGetJobName(newMind);
|
||||
_admin.UpdatePlayerList(player);
|
||||
|
||||
if (lateJoin && !silent)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ namespace Content.Server.IoC
|
|||
IoCManager.Register<MappingManager>();
|
||||
IoCManager.Register<IWatchlistWebhookManager, WatchlistWebhookManager>();
|
||||
IoCManager.Register<ConnectionManager>();
|
||||
IoCManager.Register<MultiServerKickManager>();
|
||||
|
||||
IoCManager.Register<ServersHubManager>(); // Sunrise-Hub
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ public sealed partial class IdCardComponent : Component
|
|||
[Access(typeof(SharedIdCardSystem), typeof(SharedPdaSystem), typeof(SharedAgentIdCardSystem), Other = AccessPermissions.ReadWrite)]
|
||||
public LocId? JobTitle;
|
||||
|
||||
[DataField]
|
||||
[AutoNetworkedField]
|
||||
private string? _jobTitle;
|
||||
|
||||
[Access(typeof(SharedIdCardSystem), typeof(SharedPdaSystem), typeof(SharedAgentIdCardSystem), Other = AccessPermissions.ReadWriteExecute)]
|
||||
|
|
|
|||
|
|
@ -182,4 +182,11 @@ public sealed partial class CCVars
|
|||
|
||||
public static readonly CVarDef<bool> BanHardwareIds =
|
||||
CVarDef.Create("ban.hardware_ids", true, CVar.SERVERONLY);
|
||||
|
||||
/// <summary>
|
||||
/// If true, players are allowed to connect to multiple game servers at once.
|
||||
/// If false, they will be kicked from the first when connecting to another.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<bool> AdminAllowMultiServerPlay =
|
||||
CVarDef.Create("admin.allow_multi_server_play", true, CVar.SERVERONLY);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,5 +24,13 @@ public sealed partial class ContrabandComponent : Component
|
|||
/// </summary>
|
||||
[DataField]
|
||||
[AutoNetworkedField]
|
||||
public HashSet<ProtoId<DepartmentPrototype>>? AllowedDepartments = ["Security"];
|
||||
public HashSet<ProtoId<DepartmentPrototype>> AllowedDepartments = new();
|
||||
|
||||
/// <summary>
|
||||
/// Which jobs is this item restricted to?
|
||||
/// If empty, no jobs are allowed to use this beyond the allowed departments.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
[AutoNetworkedField]
|
||||
public HashSet<ProtoId<JobPrototype>> AllowedJobs = new();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ public sealed partial class ContrabandSeverityPrototype : IPrototype
|
|||
public LocId ExamineText;
|
||||
|
||||
/// <summary>
|
||||
/// When examining the contraband, should this take into account the viewer's departments?
|
||||
/// When examining the contraband, should this take into account the viewer's departments and job?
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool ShowDepartments;
|
||||
public bool ShowDepartmentsAndJobs;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ public sealed class ContrabandSystem : EntitySystem
|
|||
|
||||
contraband.Severity = other.Severity;
|
||||
contraband.AllowedDepartments = other.AllowedDepartments;
|
||||
contraband.AllowedJobs = other.AllowedJobs;
|
||||
Dirty(uid, contraband);
|
||||
}
|
||||
|
||||
|
|
@ -54,11 +55,15 @@ public sealed class ContrabandSystem : EntitySystem
|
|||
|
||||
using (args.PushGroup(nameof(ContrabandComponent)))
|
||||
{
|
||||
// TODO shouldn't department prototypes have a localized name instead of just using the ID for this?
|
||||
var localizedDepartments = ent.Comp.AllowedDepartments.Select(p => Loc.GetString($"department-{p.Id}"));
|
||||
var localizedJobs = ent.Comp.AllowedJobs.Select(p => _proto.Index(p).LocalizedName);
|
||||
|
||||
var severity = _proto.Index(ent.Comp.Severity);
|
||||
if (severity.ShowDepartments && ent.Comp is { AllowedDepartments: not null })
|
||||
if (severity.ShowDepartmentsAndJobs)
|
||||
{
|
||||
// TODO shouldn't department prototypes have a localized name instead of just using the ID for this?
|
||||
var list = ContentLocalizationManager.FormatList(ent.Comp.AllowedDepartments.Select(p => Loc.GetString($"department-{p.Id}")).ToList());
|
||||
//creating a combined list of jobs and departments for the restricted text
|
||||
var list = ContentLocalizationManager.FormatList(localizedDepartments.Concat(localizedJobs).ToList());
|
||||
|
||||
// department restricted text
|
||||
args.PushMarkup(Loc.GetString("contraband-examine-text-Restricted-department", ("departments", list)));
|
||||
|
|
@ -69,23 +74,30 @@ public sealed class ContrabandSystem : EntitySystem
|
|||
}
|
||||
|
||||
// text based on ID card
|
||||
List<ProtoId<DepartmentPrototype>>? departments = null;
|
||||
List<ProtoId<DepartmentPrototype>> departments = new();
|
||||
var jobId = "";
|
||||
|
||||
if (_id.TryFindIdCard(args.Examiner, out var id))
|
||||
{
|
||||
departments = id.Comp.JobDepartments;
|
||||
if (id.Comp.LocalizedJobTitle is not null)
|
||||
{
|
||||
jobId = id.Comp.LocalizedJobTitle;
|
||||
}
|
||||
}
|
||||
|
||||
// either its fully restricted, you have no departments, or your departments dont intersect with the restricted departments
|
||||
if (ent.Comp.AllowedDepartments is null
|
||||
|| departments is null
|
||||
|| !departments.Intersect(ent.Comp.AllowedDepartments).Any())
|
||||
// for the jobs we compare the localized string in case you use an agent ID or custom job name that is not a prototype
|
||||
if (departments.Intersect(ent.Comp.AllowedDepartments).Any()
|
||||
|| localizedJobs.Contains(jobId))
|
||||
{
|
||||
args.PushMarkup(Loc.GetString("contraband-examine-text-avoid-carrying-around"));
|
||||
return;
|
||||
// you are allowed to use this!
|
||||
args.PushMarkup(Loc.GetString("contraband-examine-text-in-the-clear"));
|
||||
}
|
||||
else
|
||||
{
|
||||
// straight to jail!
|
||||
args.PushMarkup(Loc.GetString("contraband-examine-text-avoid-carrying-around"));
|
||||
}
|
||||
|
||||
// otherwise fine to use :tm:
|
||||
args.PushMarkup(Loc.GetString("contraband-examine-text-in-the-clear"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -728,5 +728,14 @@ Entries:
|
|||
id: 90
|
||||
time: '2025-01-17T07:24:50.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/33782
|
||||
- author: PJB3005
|
||||
changes:
|
||||
- message: Introduced an option to automatically disconnect players from their current
|
||||
server if they join another one within the same server community. Admins are
|
||||
always exempt.
|
||||
type: Add
|
||||
id: 91
|
||||
time: '2025-01-21T23:23:47.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/34563
|
||||
Name: Admin
|
||||
Order: 1
|
||||
|
|
|
|||
|
|
@ -1,32 +1,4 @@
|
|||
Entries:
|
||||
- author: lzk228
|
||||
changes:
|
||||
- message: The captain now have special late join message.
|
||||
type: Tweak
|
||||
id: 7335
|
||||
time: '2024-09-09T19:57:37.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/31991
|
||||
- author: Thinbug0
|
||||
changes:
|
||||
- message: Teal gloves can now be found at the ClothesMate!
|
||||
type: Add
|
||||
id: 7336
|
||||
time: '2024-09-09T21:47:53.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/31865
|
||||
- author: chillyconmor
|
||||
changes:
|
||||
- message: Space Ninjas now have a new intro song.
|
||||
type: Add
|
||||
id: 7337
|
||||
time: '2024-09-09T22:12:25.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/31055
|
||||
- author: DieselMohawk
|
||||
changes:
|
||||
- message: Made Trooper Uniform accessible for Security Officers in loadouts
|
||||
type: Fix
|
||||
id: 7338
|
||||
time: '2024-09-09T23:59:31.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/32019
|
||||
- author: IProduceWidgets
|
||||
changes:
|
||||
- message: Visitors now can have the correct Id cards and PDA!
|
||||
|
|
@ -3892,3 +3864,43 @@
|
|||
id: 7834
|
||||
time: '2025-01-20T01:24:40.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/34527
|
||||
- author: Compilatron144
|
||||
changes:
|
||||
- message: Raised Plasma Station's population limit
|
||||
type: Tweak
|
||||
id: 7835
|
||||
time: '2025-01-21T04:56:47.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/34549
|
||||
- author: sporkyz
|
||||
changes:
|
||||
- message: Galoshes are now restricted to janitor.
|
||||
type: Tweak
|
||||
- message: Double barrel shotguns, basic armor vests, beanbag/flare shotgun shells,
|
||||
and bandoliers are now allowed for Bartenders, not just Security.
|
||||
type: Tweak
|
||||
- message: Lawyers now have access to the Security headset they spawn with, as well
|
||||
as the encryption key inside.
|
||||
type: Fix
|
||||
- message: Sawn-off shotguns are now properly considered contraband instead of being
|
||||
unrestricted.
|
||||
type: Fix
|
||||
id: 7836
|
||||
time: '2025-01-21T09:51:27.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/33385
|
||||
- author: K-Dynamic
|
||||
changes:
|
||||
- message: Electrified doors and windoors now visibly spark when touched.
|
||||
type: Tweak
|
||||
- message: New tips have been added about opening electrified doors by throwing
|
||||
your PDA or ID, and resetting doors that are electrified by AI.
|
||||
type: Add
|
||||
id: 7837
|
||||
time: '2025-01-21T10:39:16.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/34502
|
||||
- author: Nimfar11
|
||||
changes:
|
||||
- message: Adds Blueprint double emergency tank.
|
||||
type: Add
|
||||
id: 7838
|
||||
time: '2025-01-21T16:08:21.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/34232
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
multi-server-kick-reason = Connected to different server in this community.
|
||||
|
|
@ -133,3 +133,5 @@ tips-dataset-132 = By right clicking on a player, and then clicking the heart ic
|
|||
tips-dataset-133 = Monkeys and kobolds have a rare chance to be sentient. Ook!
|
||||
tips-dataset-134 = You can tell if an area with firelocks up is spaced by looking to see if the firelocks have lights beside them.
|
||||
tips-dataset-135 = Instead of picking it up, you can alt-click food to eat it. This also works for mice and other creatures without hands.
|
||||
tips-dataset-136 = If you're trapped behind an electrified door, disable the APC or throw your ID at the door to avoid getting shocked!
|
||||
tips-dataset-137 = If the AI electrifies a door and you have insulated gloves, snip and mend the power wire to reset their electrification!
|
||||
|
|
|
|||
|
|
@ -2210,6 +2210,8 @@ entities:
|
|||
713: -32,-30
|
||||
714: -31,-19
|
||||
- node:
|
||||
cleanable: True
|
||||
zIndex: 1
|
||||
color: '#FFFFFFFF'
|
||||
id: Dirt
|
||||
decals:
|
||||
|
|
@ -2226,11 +2228,6 @@ entities:
|
|||
6708: -71,-37
|
||||
6709: -70,-36
|
||||
6710: -68,-36
|
||||
- node:
|
||||
cleanable: True
|
||||
color: '#FFFFFFFF'
|
||||
id: Dirt
|
||||
decals:
|
||||
5206: -56,-54
|
||||
5207: -55,-55
|
||||
5208: -55,-54
|
||||
|
|
@ -2293,6 +2290,8 @@ entities:
|
|||
7242: -21,19
|
||||
7243: -22,20
|
||||
- node:
|
||||
cleanable: True
|
||||
zIndex: 1
|
||||
color: '#FFFFFFFF'
|
||||
id: DirtHeavy
|
||||
decals:
|
||||
|
|
@ -2305,11 +2304,6 @@ entities:
|
|||
6083: -21,5
|
||||
6084: -21,4
|
||||
6085: -22,4
|
||||
- node:
|
||||
cleanable: True
|
||||
color: '#FFFFFFFF'
|
||||
id: DirtHeavy
|
||||
decals:
|
||||
329: -54,-11
|
||||
330: -50,-11
|
||||
1572: -65,-34
|
||||
|
|
@ -2365,11 +2359,14 @@ entities:
|
|||
4063: -98,-18
|
||||
- node:
|
||||
cleanable: True
|
||||
zIndex: 1
|
||||
color: '#FFFFFFFF'
|
||||
id: DirtHeavyMonotile
|
||||
decals:
|
||||
1579: -66,-34
|
||||
- node:
|
||||
cleanable: True
|
||||
zIndex: 1
|
||||
color: '#FFFFFFFF'
|
||||
id: DirtLight
|
||||
decals:
|
||||
|
|
@ -2389,11 +2386,6 @@ entities:
|
|||
6713: -71,-32
|
||||
6714: -71,-31
|
||||
6715: -71,-30
|
||||
- node:
|
||||
cleanable: True
|
||||
color: '#FFFFFFFF'
|
||||
id: DirtLight
|
||||
decals:
|
||||
1580: -64,-33
|
||||
1581: -65,-33
|
||||
1582: -64,-32
|
||||
|
|
@ -2490,6 +2482,8 @@ entities:
|
|||
6921: -153,-44
|
||||
6922: -145,-45
|
||||
- node:
|
||||
cleanable: True
|
||||
zIndex: 1
|
||||
color: '#FFFFFFFF'
|
||||
id: DirtMedium
|
||||
decals:
|
||||
|
|
@ -2652,11 +2646,6 @@ entities:
|
|||
7221: -121,34
|
||||
7222: -122,34
|
||||
7223: -122,33
|
||||
- node:
|
||||
cleanable: True
|
||||
color: '#FFFFFFFF'
|
||||
id: DirtMedium
|
||||
decals:
|
||||
331: -54,-10
|
||||
332: -51,-10
|
||||
333: -52,-10
|
||||
|
|
|
|||
|
|
@ -2,4 +2,4 @@
|
|||
id: Tips
|
||||
values:
|
||||
prefix: tips-dataset-
|
||||
count: 135
|
||||
count: 137
|
||||
|
|
|
|||
|
|
@ -577,7 +577,7 @@
|
|||
# Belts without visualizers
|
||||
|
||||
- type: entity
|
||||
parent: [ClothingBeltAmmoProviderBase, BaseRestrictedContraband]
|
||||
parent: [ClothingBeltAmmoProviderBase, BaseSecurityBartenderContraband]
|
||||
id: ClothingBeltBandolier
|
||||
name: bandolier
|
||||
description: A bandolier for holding shotgun ammunition.
|
||||
|
|
|
|||
|
|
@ -226,7 +226,7 @@
|
|||
- EncryptionKeyCommon
|
||||
|
||||
- type: entity
|
||||
parent: [ClothingHeadset, BaseRestrictedContraband]
|
||||
parent: [ClothingHeadset, BaseSecurityLawyerContraband]
|
||||
id: ClothingHeadsetSecurity
|
||||
name: security headset
|
||||
description: This is used by your elite security force.
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
# Numbers for armor here largely taken from /tg/.
|
||||
# NOTE: Half of the kind of armor you're probably thinking of is in vests.yml. These should probably be merged some day.
|
||||
|
||||
#Basic armor vest
|
||||
#Basic armor vest for inheritance
|
||||
- type: entity
|
||||
parent: [ClothingOuterBaseMedium, AllowSuitStorageClothing, BaseRestrictedContraband]
|
||||
id: ClothingOuterArmorBasic
|
||||
id: ClothingOuterArmorBase
|
||||
name: armor vest
|
||||
abstract: true
|
||||
description: A standard Type I armored vest that provides decent protection against most types of damage.
|
||||
components:
|
||||
- type: Sprite
|
||||
|
|
@ -22,6 +23,11 @@
|
|||
- type: ExplosionResistance
|
||||
damageCoefficient: 0.90
|
||||
|
||||
#Standard armor vest, allowed for security and bartenders
|
||||
- type: entity
|
||||
parent: [ BaseSecurityBartenderContraband, ClothingOuterArmorBase]
|
||||
id: ClothingOuterArmorBasic
|
||||
|
||||
#Alternate / slim basic armor vest
|
||||
- type: entity
|
||||
parent: ClothingOuterArmorBasic
|
||||
|
|
@ -58,7 +64,7 @@
|
|||
- type: GroupExamine
|
||||
|
||||
- type: entity
|
||||
parent: ClothingOuterArmorBasic
|
||||
parent: ClothingOuterArmorBase
|
||||
id: ClothingOuterArmorBulletproof
|
||||
name: bulletproof vest
|
||||
description: A Type III heavy bulletproof vest that excels in protecting the wearer against traditional projectile weaponry and explosives to a minor extent.
|
||||
|
|
@ -78,7 +84,7 @@
|
|||
damageCoefficient: 0.80
|
||||
|
||||
- type: entity
|
||||
parent: ClothingOuterArmorBasic
|
||||
parent: ClothingOuterArmorBase
|
||||
id: ClothingOuterArmorReflective
|
||||
name: reflective vest
|
||||
description: An armored vest with advanced shielding to protect against energy weapons.
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
sprite: Clothing/OuterClothing/Coats/bomber.rsi
|
||||
|
||||
- type: entity
|
||||
parent: [ClothingOuterStorageBase, AllowSuitStorageClothing, ClothingOuterArmorBasic]
|
||||
parent: [ClothingOuterStorageBase, AllowSuitStorageClothing, ClothingOuterArmorBase]
|
||||
id: ClothingOuterCoatDetective
|
||||
name: detective trenchcoat
|
||||
description: An 18th-century multi-purpose trenchcoat. Someone who wears this means serious business.
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@
|
|||
|
||||
#Detective's vest
|
||||
- type: entity
|
||||
parent: [ClothingOuterArmorBasic, BaseRestrictedContraband]
|
||||
parent: [ClothingOuterArmorBase, BaseRestrictedContraband]
|
||||
id: ClothingOuterVestDetective
|
||||
name: detective's vest
|
||||
description: A hard-boiled private investigator's armored vest.
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@
|
|||
sprite: Clothing/Shoes/Specific/cult.rsi
|
||||
|
||||
- type: entity
|
||||
parent: ClothingShoesBase
|
||||
parent: [ ClothingShoesBase, BaseJanitorContraband ]
|
||||
id: ClothingShoesGaloshes
|
||||
name: galoshes
|
||||
description: Rubber boots.
|
||||
|
|
|
|||
|
|
@ -222,6 +222,7 @@
|
|||
id: SalvageEquipmentRare
|
||||
table: !type:GroupSelector
|
||||
children:
|
||||
- id: BlueprintDoubleEmergencyTank
|
||||
- id: FultonBeacon
|
||||
- id: Fulton
|
||||
amount: !type:RangeNumberSelector
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@
|
|||
- state: robotics_label
|
||||
|
||||
- type: entity
|
||||
parent: [ EncryptionKey, BaseSecurityContraband ]
|
||||
parent: [ EncryptionKey, BaseSecurityLawyerContraband ]
|
||||
id: EncryptionKeySecurity
|
||||
name: security encryption key
|
||||
description: An encryption key used by security.
|
||||
|
|
|
|||
|
|
@ -37,3 +37,14 @@
|
|||
- type: Blueprint
|
||||
providedRecipes:
|
||||
- SeismicCharge
|
||||
|
||||
- type: entity
|
||||
parent: BaseBlueprint
|
||||
id: BlueprintDoubleEmergencyTank
|
||||
name: double emergency tank blueprint
|
||||
description: A blueprint with a schematic of a double emergency tank. It can be inserted into an autolathe.
|
||||
components:
|
||||
- type: Blueprint
|
||||
providedRecipes:
|
||||
- DoubleEmergencyOxygenTank
|
||||
- DoubleEmergencyNitrogenTank
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@
|
|||
- type: entity
|
||||
id: ShellShotgunBeanbag
|
||||
name: shell (.50 beanbag)
|
||||
parent: BaseShellHitscanShotgun
|
||||
parent: [ BaseShellHitscanShotgun, BaseSecurityBartenderContraband ] # Sunrise-Edit
|
||||
components:
|
||||
- type: Tag
|
||||
tags:
|
||||
|
|
@ -83,7 +83,7 @@
|
|||
- type: entity
|
||||
id: ShellShotgunFlare
|
||||
name: shell (.50 flare)
|
||||
parent: BaseShellShotgun
|
||||
parent: [ BaseShellShotgun, BaseSecurityBartenderContraband ]
|
||||
components:
|
||||
- type: Tag
|
||||
tags:
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@
|
|||
|
||||
- type: entity
|
||||
name: double-barreled shotgun
|
||||
parent: [BaseWeaponShotgun, BaseGunWieldable, BaseMinorContraband]
|
||||
parent: [BaseWeaponShotgun, BaseGunWieldable, BaseSecurityBartenderContraband]
|
||||
id: WeaponShotgunDoubleBarreled
|
||||
description: An immortal classic. Uses .50 shotgun shells.
|
||||
components:
|
||||
|
|
@ -203,7 +203,7 @@
|
|||
|
||||
- type: entity
|
||||
name: sawn-off shotgun
|
||||
parent: BaseWeaponShotgun
|
||||
parent: [ BaseWeaponShotgun, BaseSecurityBartenderContraband ]
|
||||
id: WeaponShotgunSawn
|
||||
description: Groovy! Uses .50 shotgun shells.
|
||||
components:
|
||||
|
|
|
|||
|
|
@ -203,7 +203,6 @@
|
|||
doAfterDuration: 4.0
|
||||
- type: Contraband
|
||||
severity: Syndicate
|
||||
allowedDepartments: null
|
||||
- type: Sprite
|
||||
sprite: Objects/Weapons/Melee/e_dagger.rsi
|
||||
layers:
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@
|
|||
components:
|
||||
- type: Contraband
|
||||
severity: Syndicate
|
||||
# no one should be carrying this around visibly!
|
||||
allowedDepartments: null
|
||||
|
||||
# minor contraband not departmentally restricted -- improvised weapons etc
|
||||
- type: entity
|
||||
|
|
@ -15,8 +13,6 @@
|
|||
components:
|
||||
- type: Contraband
|
||||
severity: Minor
|
||||
# according to space law no dept is authorized to have
|
||||
allowedDepartments: null
|
||||
|
||||
# major contraband, for things like guns or weaponry that don't belong to any department and aren't syndicate specific
|
||||
- type: entity
|
||||
|
|
@ -25,7 +21,6 @@
|
|||
components:
|
||||
- type: Contraband
|
||||
severity: Major
|
||||
allowedDepartments: null
|
||||
|
||||
# minor contraband by default restricted to security only
|
||||
- type: entity
|
||||
|
|
@ -157,6 +152,33 @@
|
|||
- type: Contraband
|
||||
allowedDepartments: [ Medical, Science ]
|
||||
|
||||
# contraband restricted by job by some degree
|
||||
- type: entity
|
||||
id: BaseSecurityBartenderContraband
|
||||
parent: BaseRestrictedContraband
|
||||
abstract: true
|
||||
components:
|
||||
- type: Contraband
|
||||
allowedDepartments: [ Security ]
|
||||
allowedJobs: [ Bartender ]
|
||||
|
||||
- type: entity
|
||||
id: BaseSecurityLawyerContraband
|
||||
parent: BaseRestrictedContraband
|
||||
abstract: true
|
||||
components:
|
||||
- type: Contraband
|
||||
allowedDepartments: [ Security ]
|
||||
allowedJobs: [ Lawyer ]
|
||||
|
||||
- type: entity
|
||||
id: BaseJanitorContraband
|
||||
parent: BaseRestrictedContraband
|
||||
abstract: true
|
||||
components:
|
||||
- type: Contraband
|
||||
allowedJobs: [ Janitor ]
|
||||
|
||||
# for ~objective items
|
||||
- type: entity
|
||||
id: BaseGrandTheftContraband
|
||||
|
|
|
|||
|
|
@ -30,11 +30,16 @@
|
|||
shader: unshaded
|
||||
- state: panel_open
|
||||
map: ["enum.WiresVisualLayers.MaintenancePanel"]
|
||||
- state: electrified
|
||||
- state: electrified_ai
|
||||
sprite: Interface/Misc/ai_hud.rsi
|
||||
shader: unshaded
|
||||
visible: false
|
||||
map: ["enum.ElectrifiedLayers.HUD"]
|
||||
- state: electrified
|
||||
sprite: Effects/electricity.rsi
|
||||
shader: unshaded
|
||||
visible: false
|
||||
map: ["enum.ElectrifiedLayers.Sparks"]
|
||||
- type: AnimationPlayer
|
||||
- type: Physics
|
||||
- type: Fixtures
|
||||
|
|
@ -77,6 +82,12 @@
|
|||
- type: NavMapDoor
|
||||
- type: DoorBolt
|
||||
- type: Appearance
|
||||
- type: GenericVisualizer
|
||||
visuals:
|
||||
enum.ElectrifiedVisuals.ShowSparks:
|
||||
enum.ElectrifiedLayers.Sparks:
|
||||
True: { visible: True }
|
||||
False: { visible: False }
|
||||
- type: WiresVisuals
|
||||
- type: ElectrocutionHUDVisuals
|
||||
- type: ApcPowerReceiver
|
||||
|
|
|
|||
|
|
@ -27,11 +27,16 @@
|
|||
shader: unshaded
|
||||
- state: panel_open
|
||||
map: ["enum.WiresVisualLayers.MaintenancePanel"]
|
||||
- state: electrified
|
||||
- state: electrified_ai
|
||||
sprite: Interface/Misc/ai_hud.rsi
|
||||
shader: unshaded
|
||||
visible: false
|
||||
map: ["enum.ElectrifiedLayers.HUD"]
|
||||
- state: electrified
|
||||
sprite: Effects/electricity.rsi
|
||||
shader: unshaded
|
||||
visible: false
|
||||
map: ["enum.ElectrifiedLayers.Sparks"]
|
||||
- type: AnimationPlayer
|
||||
- type: Physics
|
||||
- type: Fixtures
|
||||
|
|
@ -71,6 +76,12 @@
|
|||
- type: AccessReader
|
||||
containerAccessProvider: board
|
||||
- type: Appearance
|
||||
- type: GenericVisualizer
|
||||
visuals:
|
||||
enum.ElectrifiedVisuals.ShowSparks:
|
||||
enum.ElectrifiedLayers.Sparks:
|
||||
True: { visible: True }
|
||||
False: { visible: False }
|
||||
- type: WiresVisuals
|
||||
- type: ElectrocutionHUDVisuals
|
||||
- type: ApcPowerReceiver
|
||||
|
|
|
|||
|
|
@ -43,11 +43,16 @@
|
|||
map: ["enum.DoorVisualLayers.BaseEmergencyAccess"]
|
||||
- state: panel_open
|
||||
map: ["enum.WiresVisualLayers.MaintenancePanel"]
|
||||
- state: electrified
|
||||
- state: electrified_ai
|
||||
sprite: Interface/Misc/ai_hud.rsi
|
||||
shader: unshaded
|
||||
visible: false
|
||||
map: ["enum.ElectrifiedLayers.HUD"]
|
||||
- state: electrified
|
||||
sprite: Effects/electricity.rsi
|
||||
shader: unshaded
|
||||
visible: false
|
||||
map: ["enum.ElectrifiedLayers.Sparks"]
|
||||
- type: AnimationPlayer
|
||||
- type: ApcPowerReceiver
|
||||
- type: ExtensionCableReceiver
|
||||
|
|
@ -142,6 +147,12 @@
|
|||
enum.WiresUiKey.Key:
|
||||
type: WiresBoundUserInterface
|
||||
- type: Appearance
|
||||
- type: GenericVisualizer
|
||||
visuals:
|
||||
enum.ElectrifiedVisuals.ShowSparks:
|
||||
enum.ElectrifiedLayers.Sparks:
|
||||
True: { visible: True }
|
||||
False: { visible: False }
|
||||
- type: WiresVisuals
|
||||
- type: ElectrocutionHUDVisuals
|
||||
- type: Airtight
|
||||
|
|
@ -183,11 +194,16 @@
|
|||
- state: panel_open
|
||||
map: [ "enum.WiresVisualLayers.MaintenancePanel" ]
|
||||
visible: false
|
||||
- state: electrified
|
||||
- state: electrified_ai
|
||||
sprite: Interface/Misc/ai_hud.rsi
|
||||
shader: unshaded
|
||||
visible: false
|
||||
map: [ "enum.ElectrifiedLayers.HUD" ]
|
||||
- state: electrified
|
||||
sprite: Effects/electricity.rsi
|
||||
shader: unshaded
|
||||
visible: false
|
||||
map: [ "enum.ElectrifiedLayers.Sparks" ]
|
||||
- type: Damageable
|
||||
damageModifierSet: RGlass
|
||||
- type: Destructible
|
||||
|
|
@ -249,11 +265,16 @@
|
|||
- state: panel_open
|
||||
map: [ "enum.WiresVisualLayers.MaintenancePanel" ]
|
||||
visible: false
|
||||
- state: electrified
|
||||
- state: electrified_ai
|
||||
sprite: Interface/Misc/ai_hud.rsi
|
||||
shader: unshaded
|
||||
visible: false
|
||||
map: [ "enum.ElectrifiedLayers.HUD" ]
|
||||
- state: electrified
|
||||
sprite: Effects/electricity.rsi
|
||||
shader: unshaded
|
||||
visible: false
|
||||
map: [ "enum.ElectrifiedLayers.Sparks" ]
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
- trigger:
|
||||
|
|
@ -310,11 +331,16 @@
|
|||
- state: panel_open
|
||||
map: [ "enum.WiresVisualLayers.MaintenancePanel" ]
|
||||
visible: false
|
||||
- state: electrified
|
||||
- state: electrified_ai
|
||||
sprite: Interface/Misc/ai_hud.rsi
|
||||
shader: unshaded
|
||||
visible: false
|
||||
map: ["enum.ElectrifiedLayers.HUD"]
|
||||
map: [ "enum.ElectrifiedLayers.HUD" ]
|
||||
- state: electrified
|
||||
sprite: Effects/electricity.rsi
|
||||
shader: unshaded
|
||||
visible: false
|
||||
map: [ "enum.ElectrifiedLayers.Sparks" ]
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
- trigger:
|
||||
|
|
@ -376,11 +402,16 @@
|
|||
- state: panel_open
|
||||
map: [ "enum.WiresVisualLayers.MaintenancePanel" ]
|
||||
visible: false
|
||||
- state: electrified
|
||||
- state: electrified_ai
|
||||
sprite: Interface/Misc/ai_hud.rsi
|
||||
shader: unshaded
|
||||
visible: false
|
||||
map: [ "enum.ElectrifiedLayers.HUD" ]
|
||||
- state: electrified
|
||||
sprite: Effects/electricity.rsi
|
||||
shader: unshaded
|
||||
visible: false
|
||||
map: [ "enum.ElectrifiedLayers.Sparks" ]
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
- trigger:
|
||||
|
|
@ -437,11 +468,16 @@
|
|||
- state: panel_open
|
||||
map: [ "enum.WiresVisualLayers.MaintenancePanel" ]
|
||||
visible: false
|
||||
- state: electrified
|
||||
- state: electrified_ai
|
||||
sprite: Interface/Misc/ai_hud.rsi
|
||||
shader: unshaded
|
||||
visible: false
|
||||
map: [ "enum.ElectrifiedLayers.HUD" ]
|
||||
- state: electrified
|
||||
sprite: Effects/electricity.rsi
|
||||
shader: unshaded
|
||||
visible: false
|
||||
map: [ "enum.ElectrifiedLayers.Sparks" ]
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
- trigger:
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@
|
|||
id: Plasma
|
||||
mapName: 'Plasma'
|
||||
mapPath: /Maps/plasma.yml
|
||||
minPlayers: 20
|
||||
maxPlayers: 60
|
||||
minPlayers: 30
|
||||
stations:
|
||||
Plasma:
|
||||
stationProto: StandardNanotrasenStation
|
||||
|
|
@ -18,10 +17,16 @@
|
|||
- type: StationCargoShuttle
|
||||
path: /Maps/Shuttles/cargo_plasma.yml
|
||||
- type: StationJobs
|
||||
availableJobs:
|
||||
#service
|
||||
availableJobs: #Total of 66 jobs roundstart, max of 93 inc. latejoins and trainees.
|
||||
#command - 7
|
||||
Captain: [ 1, 1 ]
|
||||
HeadOfPersonnel: [ 1, 1 ]
|
||||
ChiefEngineer: [ 1, 1 ]
|
||||
ChiefMedicalOfficer: [ 1, 1 ]
|
||||
ResearchDirector: [ 1, 1 ]
|
||||
HeadOfSecurity: [ 1, 1 ]
|
||||
Quartermaster: [ 1, 1 ]
|
||||
#service - 18-21
|
||||
Bartender: [ 2, 2 ]
|
||||
Botanist: [ 2, 3 ]
|
||||
Chef: [ 2, 2 ]
|
||||
|
|
@ -30,37 +35,32 @@
|
|||
Librarian: [ 1, 1 ]
|
||||
ServiceWorker: [ 2, 2 ]
|
||||
Reporter: [ 2, 3 ]
|
||||
#engineering
|
||||
ChiefEngineer: [ 1, 1 ]
|
||||
Clown: [ 1, 2 ] # This might be fun
|
||||
Mime: [ 1, 1 ]
|
||||
Musician: [ 1, 1 ]
|
||||
#engineering - 8-12
|
||||
AtmosphericTechnician: [ 4, 4 ]
|
||||
StationEngineer: [ 4, 4 ]
|
||||
TechnicalAssistant: [ 4, 4 ]
|
||||
#medical
|
||||
ChiefMedicalOfficer: [ 1, 1 ]
|
||||
#medical - 9-13
|
||||
Chemist: [ 2, 2 ]
|
||||
MedicalDoctor: [ 4, 4 ]
|
||||
Paramedic: [ 2, 2 ]
|
||||
MedicalIntern: [ 4, 4 ]
|
||||
Psychologist: [ 1, 1 ]
|
||||
#science
|
||||
ResearchDirector: [ 1, 1 ]
|
||||
#science - 7-13
|
||||
Scientist: [ 4, 4 ]
|
||||
ResearchAssistant: [ 4, 4 ]
|
||||
StationAi: [ 1, 1 ]
|
||||
Borg: [ 2, 4 ]
|
||||
#security
|
||||
HeadOfSecurity: [ 1, 1 ]
|
||||
#security - 9-17
|
||||
Warden: [ 1, 1 ]
|
||||
SecurityOfficer: [ 6, 8 ]
|
||||
Detective: [ 1, 2 ]
|
||||
SecurityCadet: [ 4, 4 ]
|
||||
Lawyer: [ 1, 2 ]
|
||||
#supply
|
||||
Quartermaster: [ 1, 1 ]
|
||||
#supply - 8-10
|
||||
SalvageSpecialist: [ 4, 4 ]
|
||||
CargoTechnician: [ 4, 6 ]
|
||||
#civilian
|
||||
#civilian - the tiders yearn for the mines
|
||||
Passenger: [ -1, -1 ]
|
||||
Clown: [ 1, 1 ]
|
||||
Mime: [ 1, 1 ]
|
||||
Musician: [ 1, 1 ]
|
||||
|
|
|
|||
|
|
@ -119,6 +119,20 @@
|
|||
materials:
|
||||
Steel: 300
|
||||
|
||||
- type: latheRecipe
|
||||
id: DoubleEmergencyOxygenTank
|
||||
result: DoubleEmergencyOxygenTank
|
||||
completetime: 4
|
||||
materials:
|
||||
Steel: 250
|
||||
|
||||
- type: latheRecipe
|
||||
id: DoubleEmergencyNitrogenTank
|
||||
result: DoubleEmergencyNitrogenTank
|
||||
completetime: 4
|
||||
materials:
|
||||
Steel: 250
|
||||
|
||||
- type: latheRecipe
|
||||
id: ClothingShoesBootsMagSci
|
||||
result: ClothingShoesBootsMagSci
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
- type: contrabandSeverity
|
||||
id: Restricted
|
||||
examineText: contraband-examine-text-Restricted
|
||||
showDepartments: true
|
||||
showDepartmentsAndJobs: true
|
||||
|
||||
# Having this as a regular crew member is considered grand theft. (nuke disk, captain's gear, objective items, etc)
|
||||
- type: contrabandSeverity
|
||||
|
|
|
|||
|
|
@ -12,20 +12,20 @@
|
|||
range: 30 # 30 days
|
||||
action: Deny
|
||||
includeSecret: false
|
||||
- !type:ConditionNotesPlaytimeRange # Deny for >=2 medium severity notes in the last 14 days
|
||||
- !type:ConditionNotesPlaytimeRange # Deny for >=3 medium severity notes in the last 90 days
|
||||
includeExpired: false
|
||||
minimumSeverity: 2 # Medium
|
||||
minimumNotes: 1
|
||||
range: 14 # 14 Days
|
||||
action: Deny
|
||||
includeSecret: false
|
||||
- !type:ConditionNotesPlaytimeRange # Deny for >=3 low severity notes in the last 14 days
|
||||
includeExpired: false
|
||||
minimumSeverity: 1 # Low
|
||||
minimumNotes: 3
|
||||
range: 14 # 14 Days
|
||||
range: 90 # 90 Days
|
||||
action: Deny
|
||||
includeSecret: false
|
||||
# - !type:ConditionNotesPlaytimeRange # Deny for >=3 low severity notes in the last 14 days
|
||||
# includeExpired: false
|
||||
# minimumSeverity: 1 # Low
|
||||
# minimumNotes: 3
|
||||
# range: 14 # 14 Days
|
||||
# action: Deny
|
||||
# includeSecret: false
|
||||
- !type:ConditionManualWhitelistMembership # Allow whitelisted players
|
||||
action: Allow
|
||||
- !type:ConditionPlayerCount # Allow when <= 15 players are online
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 812 B After Width: | Height: | Size: 812 B |
|
|
@ -8,7 +8,7 @@
|
|||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "electrified",
|
||||
"name": "electrified_ai",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
|
|
@ -34,4 +34,4 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue