diff --git a/Content.Client/Administration/UI/BanPanel/BanPanel.xaml.cs b/Content.Client/Administration/UI/BanPanel/BanPanel.xaml.cs index dc263d6055..a034f503b4 100644 --- a/Content.Client/Administration/UI/BanPanel/BanPanel.xaml.cs +++ b/Content.Client/Administration/UI/BanPanel/BanPanel.xaml.cs @@ -22,11 +22,11 @@ namespace Content.Client.Administration.UI.BanPanel; [GenerateTypedNameReferences] public sealed partial class BanPanel : DefaultWindow { - public event Action? BanSubmitted; + public event Action? BanSubmitted; public event Action? PlayerChanged; private string? PlayerUsername { get; set; } private (IPAddress, int)? IpAddress { get; set; } - private byte[]? Hwid { get; set; } + private ImmutableTypedHwid? Hwid { get; set; } private double TimeEntered { get; set; } private uint Multiplier { get; set; } private bool HasBanFlag { get; set; } @@ -371,9 +371,8 @@ private void OnIpChanged() private void OnHwidChanged() { var hwidString = HwidLine.Text; - var length = 3 * (hwidString.Length / 4) - hwidString.TakeLast(2).Count(c => c == '='); - Hwid = new byte[length]; - if (HwidCheckbox.Pressed && !(string.IsNullOrEmpty(hwidString) && LastConnCheckbox.Pressed) && !Convert.TryFromBase64String(hwidString, Hwid, out _)) + ImmutableTypedHwid? hwid = null; + if (HwidCheckbox.Pressed && !(string.IsNullOrEmpty(hwidString) && LastConnCheckbox.Pressed) && !ImmutableTypedHwid.TryParse(hwidString, out hwid)) { ErrorLevel |= ErrorLevelEnum.Hwid; HwidLine.ModulateSelfOverride = Color.Red; @@ -390,7 +389,7 @@ private void OnHwidChanged() Hwid = null; return; } - Hwid = Convert.FromHexString(hwidString); + Hwid = hwid; } private void OnTypeChanged() diff --git a/Content.IntegrationTests/Tests/Commands/PardonCommand.cs b/Content.IntegrationTests/Tests/Commands/PardonCommand.cs index 4db9eabf5c..9e57cd4b0e 100644 --- a/Content.IntegrationTests/Tests/Commands/PardonCommand.cs +++ b/Content.IntegrationTests/Tests/Commands/PardonCommand.cs @@ -32,9 +32,9 @@ public async Task PardonTest() // No bans on record Assert.Multiple(async () => { - Assert.That(await sDatabase.GetServerBanAsync(null, clientId, null), Is.Null); + Assert.That(await sDatabase.GetServerBanAsync(null, clientId, null, null), Is.Null); Assert.That(await sDatabase.GetServerBanAsync(1), Is.Null); - Assert.That(await sDatabase.GetServerBansAsync(null, clientId, null), Is.Empty); + Assert.That(await sDatabase.GetServerBansAsync(null, clientId, null, null), Is.Empty); }); // Try to pardon a ban that does not exist @@ -43,9 +43,9 @@ public async Task PardonTest() // Still no bans on record Assert.Multiple(async () => { - Assert.That(await sDatabase.GetServerBanAsync(null, clientId, null), Is.Null); + Assert.That(await sDatabase.GetServerBanAsync(null, clientId, null, null), Is.Null); Assert.That(await sDatabase.GetServerBanAsync(1), Is.Null); - Assert.That(await sDatabase.GetServerBansAsync(null, clientId, null), Is.Empty); + Assert.That(await sDatabase.GetServerBansAsync(null, clientId, null, null), Is.Empty); }); var banReason = "test"; @@ -57,9 +57,9 @@ public async Task PardonTest() // Should have one ban on record now Assert.Multiple(async () => { - Assert.That(await sDatabase.GetServerBanAsync(null, clientId, null), Is.Not.Null); + Assert.That(await sDatabase.GetServerBanAsync(null, clientId, null, null), Is.Not.Null); Assert.That(await sDatabase.GetServerBanAsync(1), Is.Not.Null); - Assert.That(await sDatabase.GetServerBansAsync(null, clientId, null), Has.Count.EqualTo(1)); + Assert.That(await sDatabase.GetServerBansAsync(null, clientId, null, null), Has.Count.EqualTo(1)); }); await pair.RunTicksSync(5); @@ -70,13 +70,13 @@ public async Task PardonTest() await server.WaitPost(() => sConsole.ExecuteCommand("pardon 2")); // The existing ban is unaffected - Assert.That(await sDatabase.GetServerBanAsync(null, clientId, null), Is.Not.Null); + Assert.That(await sDatabase.GetServerBanAsync(null, clientId, null, null), Is.Not.Null); var ban = await sDatabase.GetServerBanAsync(1); Assert.Multiple(async () => { Assert.That(ban, Is.Not.Null); - Assert.That(await sDatabase.GetServerBansAsync(null, clientId, null), Has.Count.EqualTo(1)); + Assert.That(await sDatabase.GetServerBansAsync(null, clientId, null, null), Has.Count.EqualTo(1)); // Check that it matches Assert.That(ban.Id, Is.EqualTo(1)); @@ -95,7 +95,7 @@ public async Task PardonTest() await server.WaitPost(() => sConsole.ExecuteCommand("pardon 1")); // No bans should be returned - Assert.That(await sDatabase.GetServerBanAsync(null, clientId, null), Is.Null); + Assert.That(await sDatabase.GetServerBanAsync(null, clientId, null, null), Is.Null); // Direct id lookup returns a pardoned ban var pardonedBan = await sDatabase.GetServerBanAsync(1); @@ -105,7 +105,7 @@ public async Task PardonTest() Assert.That(pardonedBan, Is.Not.Null); // The list is still returned since that ignores pardons - Assert.That(await sDatabase.GetServerBansAsync(null, clientId, null), Has.Count.EqualTo(1)); + Assert.That(await sDatabase.GetServerBansAsync(null, clientId, null, null), Has.Count.EqualTo(1)); Assert.That(pardonedBan.Id, Is.EqualTo(1)); Assert.That(pardonedBan.UserId, Is.EqualTo(clientId)); @@ -133,13 +133,13 @@ public async Task PardonTest() Assert.Multiple(async () => { // No bans should be returned - Assert.That(await sDatabase.GetServerBanAsync(null, clientId, null), Is.Null); + Assert.That(await sDatabase.GetServerBanAsync(null, clientId, null, null), Is.Null); // Direct id lookup returns a pardoned ban Assert.That(await sDatabase.GetServerBanAsync(1), Is.Not.Null); // The list is still returned since that ignores pardons - Assert.That(await sDatabase.GetServerBansAsync(null, clientId, null), Has.Count.EqualTo(1)); + Assert.That(await sDatabase.GetServerBansAsync(null, clientId, null, null), Has.Count.EqualTo(1)); }); // Reconnect client. Slightly faster than dirtying the pair. diff --git a/Content.Server.Database/Migrations/Postgres/20241111170112_ModernHwid.Designer.cs b/Content.Server.Database/Migrations/Postgres/20241111170112_ModernHwid.Designer.cs new file mode 100644 index 0000000000..155d6a163f --- /dev/null +++ b/Content.Server.Database/Migrations/Postgres/20241111170112_ModernHwid.Designer.cs @@ -0,0 +1,2072 @@ +// +using System; +using System.Net; +using System.Text.Json; +using Content.Server.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using NpgsqlTypes; + +#nullable disable + +namespace Content.Server.Database.Migrations.Postgres +{ + [DbContext(typeof(PostgresServerDbContext))] + [Migration("20241111170112_ModernHwid")] + partial class ModernHwid + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.Property("AdminRankId") + .HasColumnType("integer") + .HasColumnName("admin_rank_id"); + + b.Property("Title") + .HasColumnType("text") + .HasColumnName("title"); + + b.HasKey("UserId") + .HasName("PK_admin"); + + b.HasIndex("AdminRankId") + .HasDatabaseName("IX_admin_admin_rank_id"); + + b.ToTable("admin", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminFlag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_flag_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AdminId") + .HasColumnType("uuid") + .HasColumnName("admin_id"); + + b.Property("Flag") + .IsRequired() + .HasColumnType("text") + .HasColumnName("flag"); + + b.Property("Negative") + .HasColumnType("boolean") + .HasColumnName("negative"); + + b.HasKey("Id") + .HasName("PK_admin_flag"); + + b.HasIndex("AdminId") + .HasDatabaseName("IX_admin_flag_admin_id"); + + b.HasIndex("Flag", "AdminId") + .IsUnique(); + + b.ToTable("admin_flag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("Id") + .HasColumnType("integer") + .HasColumnName("admin_log_id"); + + b.Property("Date") + .HasColumnType("timestamp with time zone") + .HasColumnName("date"); + + b.Property("Impact") + .HasColumnType("smallint") + .HasColumnName("impact"); + + b.Property("Json") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("json"); + + b.Property("Message") + .IsRequired() + .HasColumnType("text") + .HasColumnName("message"); + + b.Property("Type") + .HasColumnType("integer") + .HasColumnName("type"); + + b.HasKey("RoundId", "Id") + .HasName("PK_admin_log"); + + b.HasIndex("Date"); + + b.HasIndex("Message") + .HasAnnotation("Npgsql:TsVectorConfig", "english"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Message"), "GIN"); + + b.HasIndex("Type") + .HasDatabaseName("IX_admin_log_type"); + + b.ToTable("admin_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b => + { + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("LogId") + .HasColumnType("integer") + .HasColumnName("log_id"); + + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.HasKey("RoundId", "LogId", "PlayerUserId") + .HasName("PK_admin_log_player"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_log_player_player_user_id"); + + b.ToTable("admin_log_player", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_messages_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("uuid") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("boolean") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("uuid") + .HasColumnName("deleted_by_id"); + + b.Property("Dismissed") + .HasColumnType("boolean") + .HasColumnName("dismissed"); + + b.Property("ExpirationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiration_time"); + + b.Property("LastEditedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("uuid") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("interval") + .HasColumnName("playtime_at_note"); + + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("Seen") + .HasColumnType("boolean") + .HasColumnName("seen"); + + b.HasKey("Id") + .HasName("PK_admin_messages"); + + b.HasIndex("CreatedById"); + + b.HasIndex("DeletedById"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_messages_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_messages_round_id"); + + b.ToTable("admin_messages", null, t => + { + t.HasCheckConstraint("NotDismissedAndSeen", "NOT dismissed OR seen"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.AdminNote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_notes_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("uuid") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("boolean") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("uuid") + .HasColumnName("deleted_by_id"); + + b.Property("ExpirationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiration_time"); + + b.Property("LastEditedAt") + .IsRequired() + .HasColumnType("timestamp with time zone") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("uuid") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("interval") + .HasColumnName("playtime_at_note"); + + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("Secret") + .HasColumnType("boolean") + .HasColumnName("secret"); + + b.Property("Severity") + .HasColumnType("integer") + .HasColumnName("severity"); + + b.HasKey("Id") + .HasName("PK_admin_notes"); + + b.HasIndex("CreatedById"); + + b.HasIndex("DeletedById"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_notes_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_notes_round_id"); + + b.ToTable("admin_notes", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRank", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_rank_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("PK_admin_rank"); + + b.ToTable("admin_rank", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_rank_flag_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AdminRankId") + .HasColumnType("integer") + .HasColumnName("admin_rank_id"); + + b.Property("Flag") + .IsRequired() + .HasColumnType("text") + .HasColumnName("flag"); + + b.HasKey("Id") + .HasName("PK_admin_rank_flag"); + + b.HasIndex("AdminRankId"); + + b.HasIndex("Flag", "AdminRankId") + .IsUnique(); + + b.ToTable("admin_rank_flag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminWatchlist", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_watchlists_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("uuid") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("boolean") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("uuid") + .HasColumnName("deleted_by_id"); + + b.Property("ExpirationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiration_time"); + + b.Property("LastEditedAt") + .IsRequired() + .HasColumnType("timestamp with time zone") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("uuid") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("interval") + .HasColumnName("playtime_at_note"); + + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.HasKey("Id") + .HasName("PK_admin_watchlists"); + + b.HasIndex("CreatedById"); + + b.HasIndex("DeletedById"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_watchlists_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_watchlists_round_id"); + + b.ToTable("admin_watchlists", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Antag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("antag_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AntagName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("antag_name"); + + b.Property("ProfileId") + .HasColumnType("integer") + .HasColumnName("profile_id"); + + b.HasKey("Id") + .HasName("PK_antag"); + + b.HasIndex("ProfileId", "AntagName") + .IsUnique(); + + b.ToTable("antag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AssignedUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("assigned_user_id_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("user_name"); + + b.HasKey("Id") + .HasName("PK_assigned_user_id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.HasIndex("UserName") + .IsUnique(); + + b.ToTable("assigned_user_id", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.BanTemplate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("ban_template_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AutoDelete") + .HasColumnType("boolean") + .HasColumnName("auto_delete"); + + b.Property("ExemptFlags") + .HasColumnType("integer") + .HasColumnName("exempt_flags"); + + b.Property("Hidden") + .HasColumnType("boolean") + .HasColumnName("hidden"); + + b.Property("Length") + .HasColumnType("interval") + .HasColumnName("length"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("text") + .HasColumnName("reason"); + + b.Property("Severity") + .HasColumnType("integer") + .HasColumnName("severity"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text") + .HasColumnName("title"); + + b.HasKey("Id") + .HasName("PK_ban_template"); + + b.ToTable("ban_template", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Blacklist", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("UserId") + .HasName("PK_blacklist"); + + b.ToTable("blacklist", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("connection_log_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .IsRequired() + .HasColumnType("inet") + .HasColumnName("address"); + + b.Property("Denied") + .HasColumnType("smallint") + .HasColumnName("denied"); + + b.Property("ServerId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasColumnName("server_id"); + + b.Property("Time") + .HasColumnType("timestamp with time zone") + .HasColumnName("time"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("user_name"); + + b.HasKey("Id") + .HasName("PK_connection_log"); + + b.HasIndex("ServerId") + .HasDatabaseName("IX_connection_log_server_id"); + + b.HasIndex("Time"); + + b.HasIndex("UserId"); + + b.ToTable("connection_log", null, t => + { + t.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.Job", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("job_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("JobName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("job_name"); + + b.Property("Priority") + .HasColumnType("integer") + .HasColumnName("priority"); + + b.Property("ProfileId") + .HasColumnType("integer") + .HasColumnName("profile_id"); + + b.HasKey("Id") + .HasName("PK_job"); + + b.HasIndex("ProfileId"); + + b.HasIndex("ProfileId", "JobName") + .IsUnique(); + + b.HasIndex(new[] { "ProfileId" }, "IX_job_one_high_priority") + .IsUnique() + .HasFilter("priority = 3"); + + b.ToTable("job", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.PlayTime", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("play_time_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("PlayerId") + .HasColumnType("uuid") + .HasColumnName("player_id"); + + b.Property("TimeSpent") + .HasColumnType("interval") + .HasColumnName("time_spent"); + + b.Property("Tracker") + .IsRequired() + .HasColumnType("text") + .HasColumnName("tracker"); + + b.HasKey("Id") + .HasName("PK_play_time"); + + b.HasIndex("PlayerId", "Tracker") + .IsUnique(); + + b.ToTable("play_time", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("player_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FirstSeenTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("first_seen_time"); + + b.Property("LastReadRules") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_read_rules"); + + b.Property("LastSeenAddress") + .IsRequired() + .HasColumnType("inet") + .HasColumnName("last_seen_address"); + + b.Property("LastSeenTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_seen_time"); + + b.Property("LastSeenUserName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("last_seen_user_name"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_player"); + + b.HasAlternateKey("UserId") + .HasName("ak_player_user_id"); + + b.HasIndex("LastSeenUserName"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("player", null, t => + { + t.HasCheckConstraint("LastSeenAddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= last_seen_address"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.Preference", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("preference_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AdminOOCColor") + .IsRequired() + .HasColumnType("text") + .HasColumnName("admin_ooc_color"); + + b.Property("SelectedCharacterSlot") + .HasColumnType("integer") + .HasColumnName("selected_character_slot"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_preference"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("preference", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("profile_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Age") + .HasColumnType("integer") + .HasColumnName("age"); + + b.Property("CharacterName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("char_name"); + + b.Property("EyeColor") + .IsRequired() + .HasColumnType("text") + .HasColumnName("eye_color"); + + b.Property("FacialHairColor") + .IsRequired() + .HasColumnType("text") + .HasColumnName("facial_hair_color"); + + b.Property("FacialHairName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("facial_hair_name"); + + b.Property("FlavorText") + .IsRequired() + .HasColumnType("text") + .HasColumnName("flavor_text"); + + b.Property("Gender") + .IsRequired() + .HasColumnType("text") + .HasColumnName("gender"); + + b.Property("HairColor") + .IsRequired() + .HasColumnType("text") + .HasColumnName("hair_color"); + + b.Property("HairName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("hair_name"); + + b.Property("Markings") + .HasColumnType("jsonb") + .HasColumnName("markings"); + + b.Property("PreferenceId") + .HasColumnType("integer") + .HasColumnName("preference_id"); + + b.Property("PreferenceUnavailable") + .HasColumnType("integer") + .HasColumnName("pref_unavailable"); + + b.Property("Sex") + .IsRequired() + .HasColumnType("text") + .HasColumnName("sex"); + + b.Property("SkinColor") + .IsRequired() + .HasColumnType("text") + .HasColumnName("skin_color"); + + b.Property("Slot") + .HasColumnType("integer") + .HasColumnName("slot"); + + b.Property("SpawnPriority") + .HasColumnType("integer") + .HasColumnName("spawn_priority"); + + b.Property("Species") + .IsRequired() + .HasColumnType("text") + .HasColumnName("species"); + + b.HasKey("Id") + .HasName("PK_profile"); + + b.HasIndex("PreferenceId") + .HasDatabaseName("IX_profile_preference_id"); + + b.HasIndex("Slot", "PreferenceId") + .IsUnique(); + + b.ToTable("profile", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileLoadout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("profile_loadout_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("LoadoutName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("loadout_name"); + + b.Property("ProfileLoadoutGroupId") + .HasColumnType("integer") + .HasColumnName("profile_loadout_group_id"); + + b.HasKey("Id") + .HasName("PK_profile_loadout"); + + b.HasIndex("ProfileLoadoutGroupId"); + + b.ToTable("profile_loadout", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileLoadoutGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("profile_loadout_group_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("GroupName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("group_name"); + + b.Property("ProfileRoleLoadoutId") + .HasColumnType("integer") + .HasColumnName("profile_role_loadout_id"); + + b.HasKey("Id") + .HasName("PK_profile_loadout_group"); + + b.HasIndex("ProfileRoleLoadoutId"); + + b.ToTable("profile_loadout_group", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileRoleLoadout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("profile_role_loadout_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ProfileId") + .HasColumnType("integer") + .HasColumnName("profile_id"); + + b.Property("RoleName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("role_name"); + + b.HasKey("Id") + .HasName("PK_profile_role_loadout"); + + b.HasIndex("ProfileId"); + + b.ToTable("profile_role_loadout", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.RoleWhitelist", b => + { + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("RoleId") + .HasColumnType("text") + .HasColumnName("role_id"); + + b.HasKey("PlayerUserId", "RoleId") + .HasName("PK_role_whitelists"); + + b.ToTable("role_whitelists", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("round_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ServerId") + .HasColumnType("integer") + .HasColumnName("server_id"); + + b.Property("StartDate") + .HasColumnType("timestamp with time zone") + .HasColumnName("start_date"); + + b.HasKey("Id") + .HasName("PK_round"); + + b.HasIndex("ServerId") + .HasDatabaseName("IX_round_server_id"); + + b.HasIndex("StartDate"); + + b.ToTable("round", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Server", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("server_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("PK_server"); + + b.ToTable("server", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("server_ban_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .HasColumnType("inet") + .HasColumnName("address"); + + b.Property("AutoDelete") + .HasColumnType("boolean") + .HasColumnName("auto_delete"); + + b.Property("BanTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("ban_time"); + + b.Property("BanningAdmin") + .HasColumnType("uuid") + .HasColumnName("banning_admin"); + + b.Property("ExemptFlags") + .HasColumnType("integer") + .HasColumnName("exempt_flags"); + + b.Property("ExpirationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiration_time"); + + b.Property("Hidden") + .HasColumnType("boolean") + .HasColumnName("hidden"); + + b.Property("LastEditedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("uuid") + .HasColumnName("last_edited_by_id"); + + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("interval") + .HasColumnName("playtime_at_note"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("text") + .HasColumnName("reason"); + + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("Severity") + .HasColumnType("integer") + .HasColumnName("severity"); + + b.HasKey("Id") + .HasName("PK_server_ban"); + + b.HasIndex("Address"); + + b.HasIndex("BanningAdmin"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_server_ban_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_server_ban_round_id"); + + b.ToTable("server_ban", null, t => + { + t.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address"); + + t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR player_user_id IS NOT NULL OR hwid IS NOT NULL"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanExemption", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.Property("Flags") + .HasColumnType("integer") + .HasColumnName("flags"); + + b.HasKey("UserId") + .HasName("PK_server_ban_exemption"); + + b.ToTable("server_ban_exemption", null, t => + { + t.HasCheckConstraint("FlagsNotZero", "flags != 0"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanHit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("server_ban_hit_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BanId") + .HasColumnType("integer") + .HasColumnName("ban_id"); + + b.Property("ConnectionId") + .HasColumnType("integer") + .HasColumnName("connection_id"); + + b.HasKey("Id") + .HasName("PK_server_ban_hit"); + + b.HasIndex("BanId") + .HasDatabaseName("IX_server_ban_hit_ban_id"); + + b.HasIndex("ConnectionId") + .HasDatabaseName("IX_server_ban_hit_connection_id"); + + b.ToTable("server_ban_hit", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("server_role_ban_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .HasColumnType("inet") + .HasColumnName("address"); + + b.Property("BanTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("ban_time"); + + b.Property("BanningAdmin") + .HasColumnType("uuid") + .HasColumnName("banning_admin"); + + b.Property("ExpirationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiration_time"); + + b.Property("Hidden") + .HasColumnType("boolean") + .HasColumnName("hidden"); + + b.Property("LastEditedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("uuid") + .HasColumnName("last_edited_by_id"); + + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("interval") + .HasColumnName("playtime_at_note"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("text") + .HasColumnName("reason"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("text") + .HasColumnName("role_id"); + + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("Severity") + .HasColumnType("integer") + .HasColumnName("severity"); + + b.HasKey("Id") + .HasName("PK_server_role_ban"); + + b.HasIndex("Address"); + + b.HasIndex("BanningAdmin"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_server_role_ban_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_server_role_ban_round_id"); + + b.ToTable("server_role_ban", null, t => + { + t.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address"); + + t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR player_user_id IS NOT NULL OR hwid IS NOT NULL"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleUnban", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("role_unban_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BanId") + .HasColumnType("integer") + .HasColumnName("ban_id"); + + b.Property("UnbanTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("unban_time"); + + b.Property("UnbanningAdmin") + .HasColumnType("uuid") + .HasColumnName("unbanning_admin"); + + b.HasKey("Id") + .HasName("PK_server_role_unban"); + + b.HasIndex("BanId") + .IsUnique(); + + b.ToTable("server_role_unban", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerUnban", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("unban_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BanId") + .HasColumnType("integer") + .HasColumnName("ban_id"); + + b.Property("UnbanTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("unban_time"); + + b.Property("UnbanningAdmin") + .HasColumnType("uuid") + .HasColumnName("unbanning_admin"); + + b.HasKey("Id") + .HasName("PK_server_unban"); + + b.HasIndex("BanId") + .IsUnique(); + + b.ToTable("server_unban", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Trait", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("trait_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ProfileId") + .HasColumnType("integer") + .HasColumnName("profile_id"); + + b.Property("TraitName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("trait_name"); + + b.HasKey("Id") + .HasName("PK_trait"); + + b.HasIndex("ProfileId", "TraitName") + .IsUnique(); + + b.ToTable("trait", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.UploadedResourceLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("uploaded_resource_log_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Data") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("data"); + + b.Property("Date") + .HasColumnType("timestamp with time zone") + .HasColumnName("date"); + + b.Property("Path") + .IsRequired() + .HasColumnType("text") + .HasColumnName("path"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_uploaded_resource_log"); + + b.ToTable("uploaded_resource_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Whitelist", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("UserId") + .HasName("PK_whitelist"); + + b.ToTable("whitelist", (string)null); + }); + + modelBuilder.Entity("PlayerRound", b => + { + b.Property("PlayersId") + .HasColumnType("integer") + .HasColumnName("players_id"); + + b.Property("RoundsId") + .HasColumnType("integer") + .HasColumnName("rounds_id"); + + b.HasKey("PlayersId", "RoundsId") + .HasName("PK_player_round"); + + b.HasIndex("RoundsId") + .HasDatabaseName("IX_player_round_rounds_id"); + + b.ToTable("player_round", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.HasOne("Content.Server.Database.AdminRank", "AdminRank") + .WithMany("Admins") + .HasForeignKey("AdminRankId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_admin_rank_admin_rank_id"); + + b.Navigation("AdminRank"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminFlag", b => + { + b.HasOne("Content.Server.Database.Admin", "Admin") + .WithMany("Flags") + .HasForeignKey("AdminId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_flag_admin_admin_id"); + + b.Navigation("Admin"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany("AdminLogs") + .HasForeignKey("RoundId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_round_round_id"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b => + { + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminLogs") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_player_player_player_user_id"); + + b.HasOne("Content.Server.Database.AdminLog", "Log") + .WithMany("Players") + .HasForeignKey("RoundId", "LogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_player_admin_log_round_id_log_id"); + + b.Navigation("Log"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminMessage", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminMessagesCreated") + .HasForeignKey("CreatedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_messages_player_created_by_id"); + + b.HasOne("Content.Server.Database.Player", "DeletedBy") + .WithMany("AdminMessagesDeleted") + .HasForeignKey("DeletedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_messages_player_deleted_by_id"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminMessagesLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_messages_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminMessagesReceived") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_admin_messages_player_player_user_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_admin_messages_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("DeletedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Player"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminNote", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminNotesCreated") + .HasForeignKey("CreatedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_notes_player_created_by_id"); + + b.HasOne("Content.Server.Database.Player", "DeletedBy") + .WithMany("AdminNotesDeleted") + .HasForeignKey("DeletedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_notes_player_deleted_by_id"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminNotesLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_notes_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminNotesReceived") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_admin_notes_player_player_user_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_admin_notes_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("DeletedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Player"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b => + { + b.HasOne("Content.Server.Database.AdminRank", "Rank") + .WithMany("Flags") + .HasForeignKey("AdminRankId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_rank_flag_admin_rank_admin_rank_id"); + + b.Navigation("Rank"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminWatchlist", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminWatchlistsCreated") + .HasForeignKey("CreatedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_watchlists_player_created_by_id"); + + b.HasOne("Content.Server.Database.Player", "DeletedBy") + .WithMany("AdminWatchlistsDeleted") + .HasForeignKey("DeletedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_watchlists_player_deleted_by_id"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminWatchlistsLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_watchlists_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminWatchlistsReceived") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_admin_watchlists_player_player_user_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_admin_watchlists_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("DeletedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Player"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.Antag", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Antags") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_antag_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.HasOne("Content.Server.Database.Server", "Server") + .WithMany("ConnectionLogs") + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.SetNull) + .IsRequired() + .HasConstraintName("FK_connection_log_server_server_id"); + + b.OwnsOne("Content.Server.Database.TypedHwid", "HWId", b1 => + { + b1.Property("ConnectionLogId") + .HasColumnType("integer") + .HasColumnName("connection_log_id"); + + b1.Property("Hwid") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("hwid"); + + b1.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasColumnName("hwid_type"); + + b1.HasKey("ConnectionLogId"); + + b1.ToTable("connection_log"); + + b1.WithOwner() + .HasForeignKey("ConnectionLogId") + .HasConstraintName("FK_connection_log_connection_log_connection_log_id"); + }); + + b.Navigation("HWId"); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("Content.Server.Database.Job", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Jobs") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_job_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.Player", b => + { + b.OwnsOne("Content.Server.Database.TypedHwid", "LastSeenHWId", b1 => + { + b1.Property("PlayerId") + .HasColumnType("integer") + .HasColumnName("player_id"); + + b1.Property("Hwid") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("last_seen_hwid"); + + b1.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasColumnName("last_seen_hwid_type"); + + b1.HasKey("PlayerId"); + + b1.ToTable("player"); + + b1.WithOwner() + .HasForeignKey("PlayerId") + .HasConstraintName("FK_player_player_player_id"); + }); + + b.Navigation("LastSeenHWId"); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.HasOne("Content.Server.Database.Preference", "Preference") + .WithMany("Profiles") + .HasForeignKey("PreferenceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_profile_preference_preference_id"); + + b.Navigation("Preference"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileLoadout", b => + { + b.HasOne("Content.Server.Database.ProfileLoadoutGroup", "ProfileLoadoutGroup") + .WithMany("Loadouts") + .HasForeignKey("ProfileLoadoutGroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_profile_loadout_profile_loadout_group_profile_loadout_group~"); + + b.Navigation("ProfileLoadoutGroup"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileLoadoutGroup", b => + { + b.HasOne("Content.Server.Database.ProfileRoleLoadout", "ProfileRoleLoadout") + .WithMany("Groups") + .HasForeignKey("ProfileRoleLoadoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_profile_loadout_group_profile_role_loadout_profile_role_loa~"); + + b.Navigation("ProfileRoleLoadout"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileRoleLoadout", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Loadouts") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_profile_role_loadout_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.RoleWhitelist", b => + { + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("JobWhitelists") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_role_whitelists_player_player_user_id"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.HasOne("Content.Server.Database.Server", "Server") + .WithMany("Rounds") + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_round_server_server_id"); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminServerBansCreated") + .HasForeignKey("BanningAdmin") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_ban_player_banning_admin"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminServerBansLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_ban_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_server_ban_round_round_id"); + + b.OwnsOne("Content.Server.Database.TypedHwid", "HWId", b1 => + { + b1.Property("ServerBanId") + .HasColumnType("integer") + .HasColumnName("server_ban_id"); + + b1.Property("Hwid") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("hwid"); + + b1.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasColumnName("hwid_type"); + + b1.HasKey("ServerBanId"); + + b1.ToTable("server_ban"); + + b1.WithOwner() + .HasForeignKey("ServerBanId") + .HasConstraintName("FK_server_ban_server_ban_server_ban_id"); + }); + + b.Navigation("CreatedBy"); + + b.Navigation("HWId"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanHit", b => + { + b.HasOne("Content.Server.Database.ServerBan", "Ban") + .WithMany("BanHits") + .HasForeignKey("BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_ban_hit_server_ban_ban_id"); + + b.HasOne("Content.Server.Database.ConnectionLog", "Connection") + .WithMany("BanHits") + .HasForeignKey("ConnectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_ban_hit_connection_log_connection_id"); + + b.Navigation("Ban"); + + b.Navigation("Connection"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminServerRoleBansCreated") + .HasForeignKey("BanningAdmin") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_role_ban_player_banning_admin"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminServerRoleBansLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_role_ban_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_server_role_ban_round_round_id"); + + b.OwnsOne("Content.Server.Database.TypedHwid", "HWId", b1 => + { + b1.Property("ServerRoleBanId") + .HasColumnType("integer") + .HasColumnName("server_role_ban_id"); + + b1.Property("Hwid") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("hwid"); + + b1.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasColumnName("hwid_type"); + + b1.HasKey("ServerRoleBanId"); + + b1.ToTable("server_role_ban"); + + b1.WithOwner() + .HasForeignKey("ServerRoleBanId") + .HasConstraintName("FK_server_role_ban_server_role_ban_server_role_ban_id"); + }); + + b.Navigation("CreatedBy"); + + b.Navigation("HWId"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleUnban", b => + { + b.HasOne("Content.Server.Database.ServerRoleBan", "Ban") + .WithOne("Unban") + .HasForeignKey("Content.Server.Database.ServerRoleUnban", "BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_role_unban_server_role_ban_ban_id"); + + b.Navigation("Ban"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerUnban", b => + { + b.HasOne("Content.Server.Database.ServerBan", "Ban") + .WithOne("Unban") + .HasForeignKey("Content.Server.Database.ServerUnban", "BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_unban_server_ban_ban_id"); + + b.Navigation("Ban"); + }); + + modelBuilder.Entity("Content.Server.Database.Trait", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Traits") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_trait_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("PlayerRound", b => + { + b.HasOne("Content.Server.Database.Player", null) + .WithMany() + .HasForeignKey("PlayersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_player_round_player_players_id"); + + b.HasOne("Content.Server.Database.Round", null) + .WithMany() + .HasForeignKey("RoundsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_player_round_round_rounds_id"); + }); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.Navigation("Flags"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.Navigation("Players"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRank", b => + { + b.Navigation("Admins"); + + b.Navigation("Flags"); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.Navigation("BanHits"); + }); + + modelBuilder.Entity("Content.Server.Database.Player", b => + { + b.Navigation("AdminLogs"); + + b.Navigation("AdminMessagesCreated"); + + b.Navigation("AdminMessagesDeleted"); + + b.Navigation("AdminMessagesLastEdited"); + + b.Navigation("AdminMessagesReceived"); + + b.Navigation("AdminNotesCreated"); + + b.Navigation("AdminNotesDeleted"); + + b.Navigation("AdminNotesLastEdited"); + + b.Navigation("AdminNotesReceived"); + + b.Navigation("AdminServerBansCreated"); + + b.Navigation("AdminServerBansLastEdited"); + + b.Navigation("AdminServerRoleBansCreated"); + + b.Navigation("AdminServerRoleBansLastEdited"); + + b.Navigation("AdminWatchlistsCreated"); + + b.Navigation("AdminWatchlistsDeleted"); + + b.Navigation("AdminWatchlistsLastEdited"); + + b.Navigation("AdminWatchlistsReceived"); + + b.Navigation("JobWhitelists"); + }); + + modelBuilder.Entity("Content.Server.Database.Preference", b => + { + b.Navigation("Profiles"); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.Navigation("Antags"); + + b.Navigation("Jobs"); + + b.Navigation("Loadouts"); + + b.Navigation("Traits"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileLoadoutGroup", b => + { + b.Navigation("Loadouts"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileRoleLoadout", b => + { + b.Navigation("Groups"); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.Navigation("AdminLogs"); + }); + + modelBuilder.Entity("Content.Server.Database.Server", b => + { + b.Navigation("ConnectionLogs"); + + b.Navigation("Rounds"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.Navigation("BanHits"); + + b.Navigation("Unban"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.Navigation("Unban"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Content.Server.Database/Migrations/Postgres/20241111170112_ModernHwid.cs b/Content.Server.Database/Migrations/Postgres/20241111170112_ModernHwid.cs new file mode 100644 index 0000000000..c70a5ffaa5 --- /dev/null +++ b/Content.Server.Database/Migrations/Postgres/20241111170112_ModernHwid.cs @@ -0,0 +1,62 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Content.Server.Database.Migrations.Postgres +{ + /// + public partial class ModernHwid : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "hwid_type", + table: "server_role_ban", + type: "integer", + nullable: true, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "hwid_type", + table: "server_ban", + type: "integer", + nullable: true, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "last_seen_hwid_type", + table: "player", + type: "integer", + nullable: true, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "hwid_type", + table: "connection_log", + type: "integer", + nullable: true, + defaultValue: 0); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "hwid_type", + table: "server_role_ban"); + + migrationBuilder.DropColumn( + name: "hwid_type", + table: "server_ban"); + + migrationBuilder.DropColumn( + name: "last_seen_hwid_type", + table: "player"); + + migrationBuilder.DropColumn( + name: "hwid_type", + table: "connection_log"); + } + } +} diff --git a/Content.Server.Database/Migrations/Postgres/20241111193608_ConnectionTrust.Designer.cs b/Content.Server.Database/Migrations/Postgres/20241111193608_ConnectionTrust.Designer.cs new file mode 100644 index 0000000000..dc1b4a0eeb --- /dev/null +++ b/Content.Server.Database/Migrations/Postgres/20241111193608_ConnectionTrust.Designer.cs @@ -0,0 +1,2076 @@ +// +using System; +using System.Net; +using System.Text.Json; +using Content.Server.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using NpgsqlTypes; + +#nullable disable + +namespace Content.Server.Database.Migrations.Postgres +{ + [DbContext(typeof(PostgresServerDbContext))] + [Migration("20241111193608_ConnectionTrust")] + partial class ConnectionTrust + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.Property("AdminRankId") + .HasColumnType("integer") + .HasColumnName("admin_rank_id"); + + b.Property("Title") + .HasColumnType("text") + .HasColumnName("title"); + + b.HasKey("UserId") + .HasName("PK_admin"); + + b.HasIndex("AdminRankId") + .HasDatabaseName("IX_admin_admin_rank_id"); + + b.ToTable("admin", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminFlag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_flag_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AdminId") + .HasColumnType("uuid") + .HasColumnName("admin_id"); + + b.Property("Flag") + .IsRequired() + .HasColumnType("text") + .HasColumnName("flag"); + + b.Property("Negative") + .HasColumnType("boolean") + .HasColumnName("negative"); + + b.HasKey("Id") + .HasName("PK_admin_flag"); + + b.HasIndex("AdminId") + .HasDatabaseName("IX_admin_flag_admin_id"); + + b.HasIndex("Flag", "AdminId") + .IsUnique(); + + b.ToTable("admin_flag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("Id") + .HasColumnType("integer") + .HasColumnName("admin_log_id"); + + b.Property("Date") + .HasColumnType("timestamp with time zone") + .HasColumnName("date"); + + b.Property("Impact") + .HasColumnType("smallint") + .HasColumnName("impact"); + + b.Property("Json") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("json"); + + b.Property("Message") + .IsRequired() + .HasColumnType("text") + .HasColumnName("message"); + + b.Property("Type") + .HasColumnType("integer") + .HasColumnName("type"); + + b.HasKey("RoundId", "Id") + .HasName("PK_admin_log"); + + b.HasIndex("Date"); + + b.HasIndex("Message") + .HasAnnotation("Npgsql:TsVectorConfig", "english"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Message"), "GIN"); + + b.HasIndex("Type") + .HasDatabaseName("IX_admin_log_type"); + + b.ToTable("admin_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b => + { + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("LogId") + .HasColumnType("integer") + .HasColumnName("log_id"); + + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.HasKey("RoundId", "LogId", "PlayerUserId") + .HasName("PK_admin_log_player"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_log_player_player_user_id"); + + b.ToTable("admin_log_player", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_messages_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("uuid") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("boolean") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("uuid") + .HasColumnName("deleted_by_id"); + + b.Property("Dismissed") + .HasColumnType("boolean") + .HasColumnName("dismissed"); + + b.Property("ExpirationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiration_time"); + + b.Property("LastEditedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("uuid") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("interval") + .HasColumnName("playtime_at_note"); + + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("Seen") + .HasColumnType("boolean") + .HasColumnName("seen"); + + b.HasKey("Id") + .HasName("PK_admin_messages"); + + b.HasIndex("CreatedById"); + + b.HasIndex("DeletedById"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_messages_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_messages_round_id"); + + b.ToTable("admin_messages", null, t => + { + t.HasCheckConstraint("NotDismissedAndSeen", "NOT dismissed OR seen"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.AdminNote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_notes_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("uuid") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("boolean") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("uuid") + .HasColumnName("deleted_by_id"); + + b.Property("ExpirationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiration_time"); + + b.Property("LastEditedAt") + .IsRequired() + .HasColumnType("timestamp with time zone") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("uuid") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("interval") + .HasColumnName("playtime_at_note"); + + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("Secret") + .HasColumnType("boolean") + .HasColumnName("secret"); + + b.Property("Severity") + .HasColumnType("integer") + .HasColumnName("severity"); + + b.HasKey("Id") + .HasName("PK_admin_notes"); + + b.HasIndex("CreatedById"); + + b.HasIndex("DeletedById"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_notes_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_notes_round_id"); + + b.ToTable("admin_notes", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRank", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_rank_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("PK_admin_rank"); + + b.ToTable("admin_rank", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_rank_flag_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AdminRankId") + .HasColumnType("integer") + .HasColumnName("admin_rank_id"); + + b.Property("Flag") + .IsRequired() + .HasColumnType("text") + .HasColumnName("flag"); + + b.HasKey("Id") + .HasName("PK_admin_rank_flag"); + + b.HasIndex("AdminRankId"); + + b.HasIndex("Flag", "AdminRankId") + .IsUnique(); + + b.ToTable("admin_rank_flag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminWatchlist", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_watchlists_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("uuid") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("boolean") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("uuid") + .HasColumnName("deleted_by_id"); + + b.Property("ExpirationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiration_time"); + + b.Property("LastEditedAt") + .IsRequired() + .HasColumnType("timestamp with time zone") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("uuid") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("interval") + .HasColumnName("playtime_at_note"); + + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.HasKey("Id") + .HasName("PK_admin_watchlists"); + + b.HasIndex("CreatedById"); + + b.HasIndex("DeletedById"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_watchlists_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_watchlists_round_id"); + + b.ToTable("admin_watchlists", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Antag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("antag_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AntagName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("antag_name"); + + b.Property("ProfileId") + .HasColumnType("integer") + .HasColumnName("profile_id"); + + b.HasKey("Id") + .HasName("PK_antag"); + + b.HasIndex("ProfileId", "AntagName") + .IsUnique(); + + b.ToTable("antag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AssignedUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("assigned_user_id_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("user_name"); + + b.HasKey("Id") + .HasName("PK_assigned_user_id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.HasIndex("UserName") + .IsUnique(); + + b.ToTable("assigned_user_id", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.BanTemplate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("ban_template_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AutoDelete") + .HasColumnType("boolean") + .HasColumnName("auto_delete"); + + b.Property("ExemptFlags") + .HasColumnType("integer") + .HasColumnName("exempt_flags"); + + b.Property("Hidden") + .HasColumnType("boolean") + .HasColumnName("hidden"); + + b.Property("Length") + .HasColumnType("interval") + .HasColumnName("length"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("text") + .HasColumnName("reason"); + + b.Property("Severity") + .HasColumnType("integer") + .HasColumnName("severity"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text") + .HasColumnName("title"); + + b.HasKey("Id") + .HasName("PK_ban_template"); + + b.ToTable("ban_template", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Blacklist", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("UserId") + .HasName("PK_blacklist"); + + b.ToTable("blacklist", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("connection_log_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .IsRequired() + .HasColumnType("inet") + .HasColumnName("address"); + + b.Property("Denied") + .HasColumnType("smallint") + .HasColumnName("denied"); + + b.Property("ServerId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasColumnName("server_id"); + + b.Property("Time") + .HasColumnType("timestamp with time zone") + .HasColumnName("time"); + + b.Property("Trust") + .HasColumnType("real") + .HasColumnName("trust"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("user_name"); + + b.HasKey("Id") + .HasName("PK_connection_log"); + + b.HasIndex("ServerId") + .HasDatabaseName("IX_connection_log_server_id"); + + b.HasIndex("Time"); + + b.HasIndex("UserId"); + + b.ToTable("connection_log", null, t => + { + t.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.Job", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("job_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("JobName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("job_name"); + + b.Property("Priority") + .HasColumnType("integer") + .HasColumnName("priority"); + + b.Property("ProfileId") + .HasColumnType("integer") + .HasColumnName("profile_id"); + + b.HasKey("Id") + .HasName("PK_job"); + + b.HasIndex("ProfileId"); + + b.HasIndex("ProfileId", "JobName") + .IsUnique(); + + b.HasIndex(new[] { "ProfileId" }, "IX_job_one_high_priority") + .IsUnique() + .HasFilter("priority = 3"); + + b.ToTable("job", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.PlayTime", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("play_time_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("PlayerId") + .HasColumnType("uuid") + .HasColumnName("player_id"); + + b.Property("TimeSpent") + .HasColumnType("interval") + .HasColumnName("time_spent"); + + b.Property("Tracker") + .IsRequired() + .HasColumnType("text") + .HasColumnName("tracker"); + + b.HasKey("Id") + .HasName("PK_play_time"); + + b.HasIndex("PlayerId", "Tracker") + .IsUnique(); + + b.ToTable("play_time", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("player_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FirstSeenTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("first_seen_time"); + + b.Property("LastReadRules") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_read_rules"); + + b.Property("LastSeenAddress") + .IsRequired() + .HasColumnType("inet") + .HasColumnName("last_seen_address"); + + b.Property("LastSeenTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_seen_time"); + + b.Property("LastSeenUserName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("last_seen_user_name"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_player"); + + b.HasAlternateKey("UserId") + .HasName("ak_player_user_id"); + + b.HasIndex("LastSeenUserName"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("player", null, t => + { + t.HasCheckConstraint("LastSeenAddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= last_seen_address"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.Preference", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("preference_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AdminOOCColor") + .IsRequired() + .HasColumnType("text") + .HasColumnName("admin_ooc_color"); + + b.Property("SelectedCharacterSlot") + .HasColumnType("integer") + .HasColumnName("selected_character_slot"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_preference"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("preference", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("profile_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Age") + .HasColumnType("integer") + .HasColumnName("age"); + + b.Property("CharacterName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("char_name"); + + b.Property("EyeColor") + .IsRequired() + .HasColumnType("text") + .HasColumnName("eye_color"); + + b.Property("FacialHairColor") + .IsRequired() + .HasColumnType("text") + .HasColumnName("facial_hair_color"); + + b.Property("FacialHairName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("facial_hair_name"); + + b.Property("FlavorText") + .IsRequired() + .HasColumnType("text") + .HasColumnName("flavor_text"); + + b.Property("Gender") + .IsRequired() + .HasColumnType("text") + .HasColumnName("gender"); + + b.Property("HairColor") + .IsRequired() + .HasColumnType("text") + .HasColumnName("hair_color"); + + b.Property("HairName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("hair_name"); + + b.Property("Markings") + .HasColumnType("jsonb") + .HasColumnName("markings"); + + b.Property("PreferenceId") + .HasColumnType("integer") + .HasColumnName("preference_id"); + + b.Property("PreferenceUnavailable") + .HasColumnType("integer") + .HasColumnName("pref_unavailable"); + + b.Property("Sex") + .IsRequired() + .HasColumnType("text") + .HasColumnName("sex"); + + b.Property("SkinColor") + .IsRequired() + .HasColumnType("text") + .HasColumnName("skin_color"); + + b.Property("Slot") + .HasColumnType("integer") + .HasColumnName("slot"); + + b.Property("SpawnPriority") + .HasColumnType("integer") + .HasColumnName("spawn_priority"); + + b.Property("Species") + .IsRequired() + .HasColumnType("text") + .HasColumnName("species"); + + b.HasKey("Id") + .HasName("PK_profile"); + + b.HasIndex("PreferenceId") + .HasDatabaseName("IX_profile_preference_id"); + + b.HasIndex("Slot", "PreferenceId") + .IsUnique(); + + b.ToTable("profile", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileLoadout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("profile_loadout_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("LoadoutName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("loadout_name"); + + b.Property("ProfileLoadoutGroupId") + .HasColumnType("integer") + .HasColumnName("profile_loadout_group_id"); + + b.HasKey("Id") + .HasName("PK_profile_loadout"); + + b.HasIndex("ProfileLoadoutGroupId"); + + b.ToTable("profile_loadout", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileLoadoutGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("profile_loadout_group_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("GroupName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("group_name"); + + b.Property("ProfileRoleLoadoutId") + .HasColumnType("integer") + .HasColumnName("profile_role_loadout_id"); + + b.HasKey("Id") + .HasName("PK_profile_loadout_group"); + + b.HasIndex("ProfileRoleLoadoutId"); + + b.ToTable("profile_loadout_group", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileRoleLoadout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("profile_role_loadout_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ProfileId") + .HasColumnType("integer") + .HasColumnName("profile_id"); + + b.Property("RoleName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("role_name"); + + b.HasKey("Id") + .HasName("PK_profile_role_loadout"); + + b.HasIndex("ProfileId"); + + b.ToTable("profile_role_loadout", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.RoleWhitelist", b => + { + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("RoleId") + .HasColumnType("text") + .HasColumnName("role_id"); + + b.HasKey("PlayerUserId", "RoleId") + .HasName("PK_role_whitelists"); + + b.ToTable("role_whitelists", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("round_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ServerId") + .HasColumnType("integer") + .HasColumnName("server_id"); + + b.Property("StartDate") + .HasColumnType("timestamp with time zone") + .HasColumnName("start_date"); + + b.HasKey("Id") + .HasName("PK_round"); + + b.HasIndex("ServerId") + .HasDatabaseName("IX_round_server_id"); + + b.HasIndex("StartDate"); + + b.ToTable("round", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Server", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("server_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("PK_server"); + + b.ToTable("server", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("server_ban_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .HasColumnType("inet") + .HasColumnName("address"); + + b.Property("AutoDelete") + .HasColumnType("boolean") + .HasColumnName("auto_delete"); + + b.Property("BanTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("ban_time"); + + b.Property("BanningAdmin") + .HasColumnType("uuid") + .HasColumnName("banning_admin"); + + b.Property("ExemptFlags") + .HasColumnType("integer") + .HasColumnName("exempt_flags"); + + b.Property("ExpirationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiration_time"); + + b.Property("Hidden") + .HasColumnType("boolean") + .HasColumnName("hidden"); + + b.Property("LastEditedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("uuid") + .HasColumnName("last_edited_by_id"); + + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("interval") + .HasColumnName("playtime_at_note"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("text") + .HasColumnName("reason"); + + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("Severity") + .HasColumnType("integer") + .HasColumnName("severity"); + + b.HasKey("Id") + .HasName("PK_server_ban"); + + b.HasIndex("Address"); + + b.HasIndex("BanningAdmin"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_server_ban_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_server_ban_round_id"); + + b.ToTable("server_ban", null, t => + { + t.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address"); + + t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR player_user_id IS NOT NULL OR hwid IS NOT NULL"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanExemption", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.Property("Flags") + .HasColumnType("integer") + .HasColumnName("flags"); + + b.HasKey("UserId") + .HasName("PK_server_ban_exemption"); + + b.ToTable("server_ban_exemption", null, t => + { + t.HasCheckConstraint("FlagsNotZero", "flags != 0"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanHit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("server_ban_hit_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BanId") + .HasColumnType("integer") + .HasColumnName("ban_id"); + + b.Property("ConnectionId") + .HasColumnType("integer") + .HasColumnName("connection_id"); + + b.HasKey("Id") + .HasName("PK_server_ban_hit"); + + b.HasIndex("BanId") + .HasDatabaseName("IX_server_ban_hit_ban_id"); + + b.HasIndex("ConnectionId") + .HasDatabaseName("IX_server_ban_hit_connection_id"); + + b.ToTable("server_ban_hit", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("server_role_ban_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .HasColumnType("inet") + .HasColumnName("address"); + + b.Property("BanTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("ban_time"); + + b.Property("BanningAdmin") + .HasColumnType("uuid") + .HasColumnName("banning_admin"); + + b.Property("ExpirationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiration_time"); + + b.Property("Hidden") + .HasColumnType("boolean") + .HasColumnName("hidden"); + + b.Property("LastEditedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("uuid") + .HasColumnName("last_edited_by_id"); + + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("interval") + .HasColumnName("playtime_at_note"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("text") + .HasColumnName("reason"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("text") + .HasColumnName("role_id"); + + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("Severity") + .HasColumnType("integer") + .HasColumnName("severity"); + + b.HasKey("Id") + .HasName("PK_server_role_ban"); + + b.HasIndex("Address"); + + b.HasIndex("BanningAdmin"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_server_role_ban_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_server_role_ban_round_id"); + + b.ToTable("server_role_ban", null, t => + { + t.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address"); + + t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR player_user_id IS NOT NULL OR hwid IS NOT NULL"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleUnban", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("role_unban_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BanId") + .HasColumnType("integer") + .HasColumnName("ban_id"); + + b.Property("UnbanTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("unban_time"); + + b.Property("UnbanningAdmin") + .HasColumnType("uuid") + .HasColumnName("unbanning_admin"); + + b.HasKey("Id") + .HasName("PK_server_role_unban"); + + b.HasIndex("BanId") + .IsUnique(); + + b.ToTable("server_role_unban", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerUnban", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("unban_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BanId") + .HasColumnType("integer") + .HasColumnName("ban_id"); + + b.Property("UnbanTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("unban_time"); + + b.Property("UnbanningAdmin") + .HasColumnType("uuid") + .HasColumnName("unbanning_admin"); + + b.HasKey("Id") + .HasName("PK_server_unban"); + + b.HasIndex("BanId") + .IsUnique(); + + b.ToTable("server_unban", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Trait", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("trait_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ProfileId") + .HasColumnType("integer") + .HasColumnName("profile_id"); + + b.Property("TraitName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("trait_name"); + + b.HasKey("Id") + .HasName("PK_trait"); + + b.HasIndex("ProfileId", "TraitName") + .IsUnique(); + + b.ToTable("trait", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.UploadedResourceLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("uploaded_resource_log_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Data") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("data"); + + b.Property("Date") + .HasColumnType("timestamp with time zone") + .HasColumnName("date"); + + b.Property("Path") + .IsRequired() + .HasColumnType("text") + .HasColumnName("path"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_uploaded_resource_log"); + + b.ToTable("uploaded_resource_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Whitelist", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("UserId") + .HasName("PK_whitelist"); + + b.ToTable("whitelist", (string)null); + }); + + modelBuilder.Entity("PlayerRound", b => + { + b.Property("PlayersId") + .HasColumnType("integer") + .HasColumnName("players_id"); + + b.Property("RoundsId") + .HasColumnType("integer") + .HasColumnName("rounds_id"); + + b.HasKey("PlayersId", "RoundsId") + .HasName("PK_player_round"); + + b.HasIndex("RoundsId") + .HasDatabaseName("IX_player_round_rounds_id"); + + b.ToTable("player_round", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.HasOne("Content.Server.Database.AdminRank", "AdminRank") + .WithMany("Admins") + .HasForeignKey("AdminRankId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_admin_rank_admin_rank_id"); + + b.Navigation("AdminRank"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminFlag", b => + { + b.HasOne("Content.Server.Database.Admin", "Admin") + .WithMany("Flags") + .HasForeignKey("AdminId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_flag_admin_admin_id"); + + b.Navigation("Admin"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany("AdminLogs") + .HasForeignKey("RoundId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_round_round_id"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b => + { + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminLogs") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_player_player_player_user_id"); + + b.HasOne("Content.Server.Database.AdminLog", "Log") + .WithMany("Players") + .HasForeignKey("RoundId", "LogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_player_admin_log_round_id_log_id"); + + b.Navigation("Log"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminMessage", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminMessagesCreated") + .HasForeignKey("CreatedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_messages_player_created_by_id"); + + b.HasOne("Content.Server.Database.Player", "DeletedBy") + .WithMany("AdminMessagesDeleted") + .HasForeignKey("DeletedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_messages_player_deleted_by_id"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminMessagesLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_messages_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminMessagesReceived") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_admin_messages_player_player_user_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_admin_messages_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("DeletedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Player"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminNote", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminNotesCreated") + .HasForeignKey("CreatedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_notes_player_created_by_id"); + + b.HasOne("Content.Server.Database.Player", "DeletedBy") + .WithMany("AdminNotesDeleted") + .HasForeignKey("DeletedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_notes_player_deleted_by_id"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminNotesLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_notes_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminNotesReceived") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_admin_notes_player_player_user_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_admin_notes_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("DeletedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Player"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b => + { + b.HasOne("Content.Server.Database.AdminRank", "Rank") + .WithMany("Flags") + .HasForeignKey("AdminRankId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_rank_flag_admin_rank_admin_rank_id"); + + b.Navigation("Rank"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminWatchlist", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminWatchlistsCreated") + .HasForeignKey("CreatedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_watchlists_player_created_by_id"); + + b.HasOne("Content.Server.Database.Player", "DeletedBy") + .WithMany("AdminWatchlistsDeleted") + .HasForeignKey("DeletedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_watchlists_player_deleted_by_id"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminWatchlistsLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_watchlists_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminWatchlistsReceived") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_admin_watchlists_player_player_user_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_admin_watchlists_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("DeletedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Player"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.Antag", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Antags") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_antag_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.HasOne("Content.Server.Database.Server", "Server") + .WithMany("ConnectionLogs") + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.SetNull) + .IsRequired() + .HasConstraintName("FK_connection_log_server_server_id"); + + b.OwnsOne("Content.Server.Database.TypedHwid", "HWId", b1 => + { + b1.Property("ConnectionLogId") + .HasColumnType("integer") + .HasColumnName("connection_log_id"); + + b1.Property("Hwid") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("hwid"); + + b1.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasColumnName("hwid_type"); + + b1.HasKey("ConnectionLogId"); + + b1.ToTable("connection_log"); + + b1.WithOwner() + .HasForeignKey("ConnectionLogId") + .HasConstraintName("FK_connection_log_connection_log_connection_log_id"); + }); + + b.Navigation("HWId"); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("Content.Server.Database.Job", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Jobs") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_job_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.Player", b => + { + b.OwnsOne("Content.Server.Database.TypedHwid", "LastSeenHWId", b1 => + { + b1.Property("PlayerId") + .HasColumnType("integer") + .HasColumnName("player_id"); + + b1.Property("Hwid") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("last_seen_hwid"); + + b1.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasColumnName("last_seen_hwid_type"); + + b1.HasKey("PlayerId"); + + b1.ToTable("player"); + + b1.WithOwner() + .HasForeignKey("PlayerId") + .HasConstraintName("FK_player_player_player_id"); + }); + + b.Navigation("LastSeenHWId"); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.HasOne("Content.Server.Database.Preference", "Preference") + .WithMany("Profiles") + .HasForeignKey("PreferenceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_profile_preference_preference_id"); + + b.Navigation("Preference"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileLoadout", b => + { + b.HasOne("Content.Server.Database.ProfileLoadoutGroup", "ProfileLoadoutGroup") + .WithMany("Loadouts") + .HasForeignKey("ProfileLoadoutGroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_profile_loadout_profile_loadout_group_profile_loadout_group~"); + + b.Navigation("ProfileLoadoutGroup"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileLoadoutGroup", b => + { + b.HasOne("Content.Server.Database.ProfileRoleLoadout", "ProfileRoleLoadout") + .WithMany("Groups") + .HasForeignKey("ProfileRoleLoadoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_profile_loadout_group_profile_role_loadout_profile_role_loa~"); + + b.Navigation("ProfileRoleLoadout"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileRoleLoadout", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Loadouts") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_profile_role_loadout_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.RoleWhitelist", b => + { + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("JobWhitelists") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_role_whitelists_player_player_user_id"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.HasOne("Content.Server.Database.Server", "Server") + .WithMany("Rounds") + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_round_server_server_id"); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminServerBansCreated") + .HasForeignKey("BanningAdmin") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_ban_player_banning_admin"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminServerBansLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_ban_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_server_ban_round_round_id"); + + b.OwnsOne("Content.Server.Database.TypedHwid", "HWId", b1 => + { + b1.Property("ServerBanId") + .HasColumnType("integer") + .HasColumnName("server_ban_id"); + + b1.Property("Hwid") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("hwid"); + + b1.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasColumnName("hwid_type"); + + b1.HasKey("ServerBanId"); + + b1.ToTable("server_ban"); + + b1.WithOwner() + .HasForeignKey("ServerBanId") + .HasConstraintName("FK_server_ban_server_ban_server_ban_id"); + }); + + b.Navigation("CreatedBy"); + + b.Navigation("HWId"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanHit", b => + { + b.HasOne("Content.Server.Database.ServerBan", "Ban") + .WithMany("BanHits") + .HasForeignKey("BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_ban_hit_server_ban_ban_id"); + + b.HasOne("Content.Server.Database.ConnectionLog", "Connection") + .WithMany("BanHits") + .HasForeignKey("ConnectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_ban_hit_connection_log_connection_id"); + + b.Navigation("Ban"); + + b.Navigation("Connection"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminServerRoleBansCreated") + .HasForeignKey("BanningAdmin") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_role_ban_player_banning_admin"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminServerRoleBansLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_role_ban_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_server_role_ban_round_round_id"); + + b.OwnsOne("Content.Server.Database.TypedHwid", "HWId", b1 => + { + b1.Property("ServerRoleBanId") + .HasColumnType("integer") + .HasColumnName("server_role_ban_id"); + + b1.Property("Hwid") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("hwid"); + + b1.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasColumnName("hwid_type"); + + b1.HasKey("ServerRoleBanId"); + + b1.ToTable("server_role_ban"); + + b1.WithOwner() + .HasForeignKey("ServerRoleBanId") + .HasConstraintName("FK_server_role_ban_server_role_ban_server_role_ban_id"); + }); + + b.Navigation("CreatedBy"); + + b.Navigation("HWId"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleUnban", b => + { + b.HasOne("Content.Server.Database.ServerRoleBan", "Ban") + .WithOne("Unban") + .HasForeignKey("Content.Server.Database.ServerRoleUnban", "BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_role_unban_server_role_ban_ban_id"); + + b.Navigation("Ban"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerUnban", b => + { + b.HasOne("Content.Server.Database.ServerBan", "Ban") + .WithOne("Unban") + .HasForeignKey("Content.Server.Database.ServerUnban", "BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_unban_server_ban_ban_id"); + + b.Navigation("Ban"); + }); + + modelBuilder.Entity("Content.Server.Database.Trait", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Traits") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_trait_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("PlayerRound", b => + { + b.HasOne("Content.Server.Database.Player", null) + .WithMany() + .HasForeignKey("PlayersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_player_round_player_players_id"); + + b.HasOne("Content.Server.Database.Round", null) + .WithMany() + .HasForeignKey("RoundsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_player_round_round_rounds_id"); + }); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.Navigation("Flags"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.Navigation("Players"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRank", b => + { + b.Navigation("Admins"); + + b.Navigation("Flags"); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.Navigation("BanHits"); + }); + + modelBuilder.Entity("Content.Server.Database.Player", b => + { + b.Navigation("AdminLogs"); + + b.Navigation("AdminMessagesCreated"); + + b.Navigation("AdminMessagesDeleted"); + + b.Navigation("AdminMessagesLastEdited"); + + b.Navigation("AdminMessagesReceived"); + + b.Navigation("AdminNotesCreated"); + + b.Navigation("AdminNotesDeleted"); + + b.Navigation("AdminNotesLastEdited"); + + b.Navigation("AdminNotesReceived"); + + b.Navigation("AdminServerBansCreated"); + + b.Navigation("AdminServerBansLastEdited"); + + b.Navigation("AdminServerRoleBansCreated"); + + b.Navigation("AdminServerRoleBansLastEdited"); + + b.Navigation("AdminWatchlistsCreated"); + + b.Navigation("AdminWatchlistsDeleted"); + + b.Navigation("AdminWatchlistsLastEdited"); + + b.Navigation("AdminWatchlistsReceived"); + + b.Navigation("JobWhitelists"); + }); + + modelBuilder.Entity("Content.Server.Database.Preference", b => + { + b.Navigation("Profiles"); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.Navigation("Antags"); + + b.Navigation("Jobs"); + + b.Navigation("Loadouts"); + + b.Navigation("Traits"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileLoadoutGroup", b => + { + b.Navigation("Loadouts"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileRoleLoadout", b => + { + b.Navigation("Groups"); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.Navigation("AdminLogs"); + }); + + modelBuilder.Entity("Content.Server.Database.Server", b => + { + b.Navigation("ConnectionLogs"); + + b.Navigation("Rounds"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.Navigation("BanHits"); + + b.Navigation("Unban"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.Navigation("Unban"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Content.Server.Database/Migrations/Postgres/20241111193608_ConnectionTrust.cs b/Content.Server.Database/Migrations/Postgres/20241111193608_ConnectionTrust.cs new file mode 100644 index 0000000000..debb36aacc --- /dev/null +++ b/Content.Server.Database/Migrations/Postgres/20241111193608_ConnectionTrust.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Content.Server.Database.Migrations.Postgres +{ + /// + public partial class ConnectionTrust : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "trust", + table: "connection_log", + type: "real", + nullable: false, + defaultValue: 0f); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "trust", + table: "connection_log"); + } + } +} diff --git a/Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs b/Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs index c86d08b8dc..c39fb6872b 100644 --- a/Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs +++ b/Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs @@ -557,6 +557,19 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("ban_template", (string)null); }); + modelBuilder.Entity("Content.Server.Database.Blacklist", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("UserId") + .HasName("PK_blacklist"); + + b.ToTable("blacklist", (string)null); + }); + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => { b.Property("Id") @@ -575,10 +588,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("smallint") .HasColumnName("denied"); - b.Property("HWId") - .HasColumnType("bytea") - .HasColumnName("hwid"); - b.Property("ServerId") .ValueGeneratedOnAdd() .HasColumnType("integer") @@ -589,6 +598,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("timestamp with time zone") .HasColumnName("time"); + b.Property("Trust") + .HasColumnType("real") + .HasColumnName("trust"); + b.Property("UserId") .HasColumnType("uuid") .HasColumnName("user_id"); @@ -747,10 +760,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("inet") .HasColumnName("last_seen_address"); - b.Property("LastSeenHWId") - .HasColumnType("bytea") - .HasColumnName("last_seen_hwid"); - b.Property("LastSeenTime") .HasColumnType("timestamp with time zone") .HasColumnName("last_seen_time"); @@ -1032,10 +1041,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("timestamp with time zone") .HasColumnName("expiration_time"); - b.Property("HWId") - .HasColumnType("bytea") - .HasColumnName("hwid"); - b.Property("Hidden") .HasColumnType("boolean") .HasColumnName("hidden"); @@ -1166,10 +1171,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("timestamp with time zone") .HasColumnName("expiration_time"); - b.Property("HWId") - .HasColumnType("bytea") - .HasColumnName("hwid"); - b.Property("Hidden") .HasColumnType("boolean") .HasColumnName("hidden"); @@ -1611,6 +1612,34 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasConstraintName("FK_connection_log_server_server_id"); + b.OwnsOne("Content.Server.Database.TypedHwid", "HWId", b1 => + { + b1.Property("ConnectionLogId") + .HasColumnType("integer") + .HasColumnName("connection_log_id"); + + b1.Property("Hwid") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("hwid"); + + b1.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasColumnName("hwid_type"); + + b1.HasKey("ConnectionLogId"); + + b1.ToTable("connection_log"); + + b1.WithOwner() + .HasForeignKey("ConnectionLogId") + .HasConstraintName("FK_connection_log_connection_log_connection_log_id"); + }); + + b.Navigation("HWId"); + b.Navigation("Server"); }); @@ -1638,6 +1667,37 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Profile"); }); + modelBuilder.Entity("Content.Server.Database.Player", b => + { + b.OwnsOne("Content.Server.Database.TypedHwid", "LastSeenHWId", b1 => + { + b1.Property("PlayerId") + .HasColumnType("integer") + .HasColumnName("player_id"); + + b1.Property("Hwid") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("last_seen_hwid"); + + b1.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasColumnName("last_seen_hwid_type"); + + b1.HasKey("PlayerId"); + + b1.ToTable("player"); + + b1.WithOwner() + .HasForeignKey("PlayerId") + .HasConstraintName("FK_player_player_player_id"); + }); + + b.Navigation("LastSeenHWId"); + }); + modelBuilder.Entity("Content.Server.Database.Profile", b => { b.HasOne("Content.Server.Database.Preference", "Preference") @@ -1696,8 +1756,36 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasForeignKey("RoundId") .HasConstraintName("FK_server_ban_round_round_id"); + b.OwnsOne("Content.Server.Database.TypedHwid", "HWId", b1 => + { + b1.Property("ServerBanId") + .HasColumnType("integer") + .HasColumnName("server_ban_id"); + + b1.Property("Hwid") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("hwid"); + + b1.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasColumnName("hwid_type"); + + b1.HasKey("ServerBanId"); + + b1.ToTable("server_ban"); + + b1.WithOwner() + .HasForeignKey("ServerBanId") + .HasConstraintName("FK_server_ban_server_ban_server_ban_id"); + }); + b.Navigation("CreatedBy"); + b.Navigation("HWId"); + b.Navigation("LastEditedBy"); b.Navigation("Round"); @@ -1745,8 +1833,36 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasForeignKey("RoundId") .HasConstraintName("FK_server_role_ban_round_round_id"); + b.OwnsOne("Content.Server.Database.TypedHwid", "HWId", b1 => + { + b1.Property("ServerRoleBanId") + .HasColumnType("integer") + .HasColumnName("server_role_ban_id"); + + b1.Property("Hwid") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("hwid"); + + b1.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasColumnName("hwid_type"); + + b1.HasKey("ServerRoleBanId"); + + b1.ToTable("server_role_ban"); + + b1.WithOwner() + .HasForeignKey("ServerRoleBanId") + .HasConstraintName("FK_server_role_ban_server_role_ban_server_role_ban_id"); + }); + b.Navigation("CreatedBy"); + b.Navigation("HWId"); + b.Navigation("LastEditedBy"); b.Navigation("Round"); diff --git a/Content.Server.Database/Migrations/Sqlite/20241111170107_ModernHwid.Designer.cs b/Content.Server.Database/Migrations/Sqlite/20241111170107_ModernHwid.Designer.cs new file mode 100644 index 0000000000..56a9fe0a05 --- /dev/null +++ b/Content.Server.Database/Migrations/Sqlite/20241111170107_ModernHwid.Designer.cs @@ -0,0 +1,1995 @@ +// +using System; +using Content.Server.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Content.Server.Database.Migrations.Sqlite +{ + [DbContext(typeof(SqliteServerDbContext))] + [Migration("20241111170107_ModernHwid")] + partial class ModernHwid + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.0"); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.Property("AdminRankId") + .HasColumnType("INTEGER") + .HasColumnName("admin_rank_id"); + + b.Property("Title") + .HasColumnType("TEXT") + .HasColumnName("title"); + + b.HasKey("UserId") + .HasName("PK_admin"); + + b.HasIndex("AdminRankId") + .HasDatabaseName("IX_admin_admin_rank_id"); + + b.ToTable("admin", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminFlag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_flag_id"); + + b.Property("AdminId") + .HasColumnType("TEXT") + .HasColumnName("admin_id"); + + b.Property("Flag") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("flag"); + + b.Property("Negative") + .HasColumnType("INTEGER") + .HasColumnName("negative"); + + b.HasKey("Id") + .HasName("PK_admin_flag"); + + b.HasIndex("AdminId") + .HasDatabaseName("IX_admin_flag_admin_id"); + + b.HasIndex("Flag", "AdminId") + .IsUnique(); + + b.ToTable("admin_flag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("Id") + .HasColumnType("INTEGER") + .HasColumnName("admin_log_id"); + + b.Property("Date") + .HasColumnType("TEXT") + .HasColumnName("date"); + + b.Property("Impact") + .HasColumnType("INTEGER") + .HasColumnName("impact"); + + b.Property("Json") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("json"); + + b.Property("Message") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("message"); + + b.Property("Type") + .HasColumnType("INTEGER") + .HasColumnName("type"); + + b.HasKey("RoundId", "Id") + .HasName("PK_admin_log"); + + b.HasIndex("Date"); + + b.HasIndex("Type") + .HasDatabaseName("IX_admin_log_type"); + + b.ToTable("admin_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b => + { + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("LogId") + .HasColumnType("INTEGER") + .HasColumnName("log_id"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.HasKey("RoundId", "LogId", "PlayerUserId") + .HasName("PK_admin_log_player"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_log_player_player_user_id"); + + b.ToTable("admin_log_player", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_messages_id"); + + b.Property("CreatedAt") + .HasColumnType("TEXT") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("TEXT") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("INTEGER") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("TEXT") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("TEXT") + .HasColumnName("deleted_by_id"); + + b.Property("Dismissed") + .HasColumnType("INTEGER") + .HasColumnName("dismissed"); + + b.Property("ExpirationTime") + .HasColumnType("TEXT") + .HasColumnName("expiration_time"); + + b.Property("LastEditedAt") + .HasColumnType("TEXT") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("TEXT") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("TEXT") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("TEXT") + .HasColumnName("playtime_at_note"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("Seen") + .HasColumnType("INTEGER") + .HasColumnName("seen"); + + b.HasKey("Id") + .HasName("PK_admin_messages"); + + b.HasIndex("CreatedById"); + + b.HasIndex("DeletedById"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_messages_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_messages_round_id"); + + b.ToTable("admin_messages", null, t => + { + t.HasCheckConstraint("NotDismissedAndSeen", "NOT dismissed OR seen"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.AdminNote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_notes_id"); + + b.Property("CreatedAt") + .HasColumnType("TEXT") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("TEXT") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("INTEGER") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("TEXT") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("TEXT") + .HasColumnName("deleted_by_id"); + + b.Property("ExpirationTime") + .HasColumnType("TEXT") + .HasColumnName("expiration_time"); + + b.Property("LastEditedAt") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("TEXT") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("TEXT") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("TEXT") + .HasColumnName("playtime_at_note"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("Secret") + .HasColumnType("INTEGER") + .HasColumnName("secret"); + + b.Property("Severity") + .HasColumnType("INTEGER") + .HasColumnName("severity"); + + b.HasKey("Id") + .HasName("PK_admin_notes"); + + b.HasIndex("CreatedById"); + + b.HasIndex("DeletedById"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_notes_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_notes_round_id"); + + b.ToTable("admin_notes", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRank", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_rank_id"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("PK_admin_rank"); + + b.ToTable("admin_rank", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_rank_flag_id"); + + b.Property("AdminRankId") + .HasColumnType("INTEGER") + .HasColumnName("admin_rank_id"); + + b.Property("Flag") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("flag"); + + b.HasKey("Id") + .HasName("PK_admin_rank_flag"); + + b.HasIndex("AdminRankId"); + + b.HasIndex("Flag", "AdminRankId") + .IsUnique(); + + b.ToTable("admin_rank_flag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminWatchlist", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_watchlists_id"); + + b.Property("CreatedAt") + .HasColumnType("TEXT") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("TEXT") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("INTEGER") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("TEXT") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("TEXT") + .HasColumnName("deleted_by_id"); + + b.Property("ExpirationTime") + .HasColumnType("TEXT") + .HasColumnName("expiration_time"); + + b.Property("LastEditedAt") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("TEXT") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("TEXT") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("TEXT") + .HasColumnName("playtime_at_note"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.HasKey("Id") + .HasName("PK_admin_watchlists"); + + b.HasIndex("CreatedById"); + + b.HasIndex("DeletedById"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_watchlists_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_watchlists_round_id"); + + b.ToTable("admin_watchlists", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Antag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("antag_id"); + + b.Property("AntagName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("antag_name"); + + b.Property("ProfileId") + .HasColumnType("INTEGER") + .HasColumnName("profile_id"); + + b.HasKey("Id") + .HasName("PK_antag"); + + b.HasIndex("ProfileId", "AntagName") + .IsUnique(); + + b.ToTable("antag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AssignedUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("assigned_user_id_id"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("user_name"); + + b.HasKey("Id") + .HasName("PK_assigned_user_id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.HasIndex("UserName") + .IsUnique(); + + b.ToTable("assigned_user_id", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.BanTemplate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("ban_template_id"); + + b.Property("AutoDelete") + .HasColumnType("INTEGER") + .HasColumnName("auto_delete"); + + b.Property("ExemptFlags") + .HasColumnType("INTEGER") + .HasColumnName("exempt_flags"); + + b.Property("Hidden") + .HasColumnType("INTEGER") + .HasColumnName("hidden"); + + b.Property("Length") + .HasColumnType("TEXT") + .HasColumnName("length"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("reason"); + + b.Property("Severity") + .HasColumnType("INTEGER") + .HasColumnName("severity"); + + b.Property("Title") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("title"); + + b.HasKey("Id") + .HasName("PK_ban_template"); + + b.ToTable("ban_template", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Blacklist", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.HasKey("UserId") + .HasName("PK_blacklist"); + + b.ToTable("blacklist", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("connection_log_id"); + + b.Property("Address") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("address"); + + b.Property("Denied") + .HasColumnType("INTEGER") + .HasColumnName("denied"); + + b.Property("ServerId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0) + .HasColumnName("server_id"); + + b.Property("Time") + .HasColumnType("TEXT") + .HasColumnName("time"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("user_name"); + + b.HasKey("Id") + .HasName("PK_connection_log"); + + b.HasIndex("ServerId") + .HasDatabaseName("IX_connection_log_server_id"); + + b.HasIndex("Time"); + + b.HasIndex("UserId"); + + b.ToTable("connection_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Job", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("job_id"); + + b.Property("JobName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("job_name"); + + b.Property("Priority") + .HasColumnType("INTEGER") + .HasColumnName("priority"); + + b.Property("ProfileId") + .HasColumnType("INTEGER") + .HasColumnName("profile_id"); + + b.HasKey("Id") + .HasName("PK_job"); + + b.HasIndex("ProfileId"); + + b.HasIndex("ProfileId", "JobName") + .IsUnique(); + + b.HasIndex(new[] { "ProfileId" }, "IX_job_one_high_priority") + .IsUnique() + .HasFilter("priority = 3"); + + b.ToTable("job", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.PlayTime", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("play_time_id"); + + b.Property("PlayerId") + .HasColumnType("TEXT") + .HasColumnName("player_id"); + + b.Property("TimeSpent") + .HasColumnType("TEXT") + .HasColumnName("time_spent"); + + b.Property("Tracker") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("tracker"); + + b.HasKey("Id") + .HasName("PK_play_time"); + + b.HasIndex("PlayerId", "Tracker") + .IsUnique(); + + b.ToTable("play_time", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("player_id"); + + b.Property("FirstSeenTime") + .HasColumnType("TEXT") + .HasColumnName("first_seen_time"); + + b.Property("LastReadRules") + .HasColumnType("TEXT") + .HasColumnName("last_read_rules"); + + b.Property("LastSeenAddress") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("last_seen_address"); + + b.Property("LastSeenTime") + .HasColumnType("TEXT") + .HasColumnName("last_seen_time"); + + b.Property("LastSeenUserName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("last_seen_user_name"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_player"); + + b.HasAlternateKey("UserId") + .HasName("ak_player_user_id"); + + b.HasIndex("LastSeenUserName"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("player", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Preference", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("preference_id"); + + b.Property("AdminOOCColor") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("admin_ooc_color"); + + b.Property("SelectedCharacterSlot") + .HasColumnType("INTEGER") + .HasColumnName("selected_character_slot"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_preference"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("preference", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("profile_id"); + + b.Property("Age") + .HasColumnType("INTEGER") + .HasColumnName("age"); + + b.Property("CharacterName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("char_name"); + + b.Property("EyeColor") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("eye_color"); + + b.Property("FacialHairColor") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("facial_hair_color"); + + b.Property("FacialHairName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("facial_hair_name"); + + b.Property("FlavorText") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("flavor_text"); + + b.Property("Gender") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("gender"); + + b.Property("HairColor") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("hair_color"); + + b.Property("HairName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("hair_name"); + + b.Property("Markings") + .HasColumnType("jsonb") + .HasColumnName("markings"); + + b.Property("PreferenceId") + .HasColumnType("INTEGER") + .HasColumnName("preference_id"); + + b.Property("PreferenceUnavailable") + .HasColumnType("INTEGER") + .HasColumnName("pref_unavailable"); + + b.Property("Sex") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("sex"); + + b.Property("SkinColor") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("skin_color"); + + b.Property("Slot") + .HasColumnType("INTEGER") + .HasColumnName("slot"); + + b.Property("SpawnPriority") + .HasColumnType("INTEGER") + .HasColumnName("spawn_priority"); + + b.Property("Species") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("species"); + + b.HasKey("Id") + .HasName("PK_profile"); + + b.HasIndex("PreferenceId") + .HasDatabaseName("IX_profile_preference_id"); + + b.HasIndex("Slot", "PreferenceId") + .IsUnique(); + + b.ToTable("profile", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileLoadout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("profile_loadout_id"); + + b.Property("LoadoutName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("loadout_name"); + + b.Property("ProfileLoadoutGroupId") + .HasColumnType("INTEGER") + .HasColumnName("profile_loadout_group_id"); + + b.HasKey("Id") + .HasName("PK_profile_loadout"); + + b.HasIndex("ProfileLoadoutGroupId"); + + b.ToTable("profile_loadout", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileLoadoutGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("profile_loadout_group_id"); + + b.Property("GroupName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("group_name"); + + b.Property("ProfileRoleLoadoutId") + .HasColumnType("INTEGER") + .HasColumnName("profile_role_loadout_id"); + + b.HasKey("Id") + .HasName("PK_profile_loadout_group"); + + b.HasIndex("ProfileRoleLoadoutId"); + + b.ToTable("profile_loadout_group", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileRoleLoadout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("profile_role_loadout_id"); + + b.Property("ProfileId") + .HasColumnType("INTEGER") + .HasColumnName("profile_id"); + + b.Property("RoleName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("role_name"); + + b.HasKey("Id") + .HasName("PK_profile_role_loadout"); + + b.HasIndex("ProfileId"); + + b.ToTable("profile_role_loadout", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.RoleWhitelist", b => + { + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("RoleId") + .HasColumnType("TEXT") + .HasColumnName("role_id"); + + b.HasKey("PlayerUserId", "RoleId") + .HasName("PK_role_whitelists"); + + b.ToTable("role_whitelists", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("ServerId") + .HasColumnType("INTEGER") + .HasColumnName("server_id"); + + b.Property("StartDate") + .HasColumnType("TEXT") + .HasColumnName("start_date"); + + b.HasKey("Id") + .HasName("PK_round"); + + b.HasIndex("ServerId") + .HasDatabaseName("IX_round_server_id"); + + b.HasIndex("StartDate"); + + b.ToTable("round", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Server", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("server_id"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("PK_server"); + + b.ToTable("server", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("server_ban_id"); + + b.Property("Address") + .HasColumnType("TEXT") + .HasColumnName("address"); + + b.Property("AutoDelete") + .HasColumnType("INTEGER") + .HasColumnName("auto_delete"); + + b.Property("BanTime") + .HasColumnType("TEXT") + .HasColumnName("ban_time"); + + b.Property("BanningAdmin") + .HasColumnType("TEXT") + .HasColumnName("banning_admin"); + + b.Property("ExemptFlags") + .HasColumnType("INTEGER") + .HasColumnName("exempt_flags"); + + b.Property("ExpirationTime") + .HasColumnType("TEXT") + .HasColumnName("expiration_time"); + + b.Property("Hidden") + .HasColumnType("INTEGER") + .HasColumnName("hidden"); + + b.Property("LastEditedAt") + .HasColumnType("TEXT") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("TEXT") + .HasColumnName("last_edited_by_id"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("TEXT") + .HasColumnName("playtime_at_note"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("reason"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("Severity") + .HasColumnType("INTEGER") + .HasColumnName("severity"); + + b.HasKey("Id") + .HasName("PK_server_ban"); + + b.HasIndex("Address"); + + b.HasIndex("BanningAdmin"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_server_ban_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_server_ban_round_id"); + + b.ToTable("server_ban", null, t => + { + t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR player_user_id IS NOT NULL OR hwid IS NOT NULL"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanExemption", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.Property("Flags") + .HasColumnType("INTEGER") + .HasColumnName("flags"); + + b.HasKey("UserId") + .HasName("PK_server_ban_exemption"); + + b.ToTable("server_ban_exemption", null, t => + { + t.HasCheckConstraint("FlagsNotZero", "flags != 0"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanHit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("server_ban_hit_id"); + + b.Property("BanId") + .HasColumnType("INTEGER") + .HasColumnName("ban_id"); + + b.Property("ConnectionId") + .HasColumnType("INTEGER") + .HasColumnName("connection_id"); + + b.HasKey("Id") + .HasName("PK_server_ban_hit"); + + b.HasIndex("BanId") + .HasDatabaseName("IX_server_ban_hit_ban_id"); + + b.HasIndex("ConnectionId") + .HasDatabaseName("IX_server_ban_hit_connection_id"); + + b.ToTable("server_ban_hit", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("server_role_ban_id"); + + b.Property("Address") + .HasColumnType("TEXT") + .HasColumnName("address"); + + b.Property("BanTime") + .HasColumnType("TEXT") + .HasColumnName("ban_time"); + + b.Property("BanningAdmin") + .HasColumnType("TEXT") + .HasColumnName("banning_admin"); + + b.Property("ExpirationTime") + .HasColumnType("TEXT") + .HasColumnName("expiration_time"); + + b.Property("Hidden") + .HasColumnType("INTEGER") + .HasColumnName("hidden"); + + b.Property("LastEditedAt") + .HasColumnType("TEXT") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("TEXT") + .HasColumnName("last_edited_by_id"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("TEXT") + .HasColumnName("playtime_at_note"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("reason"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("role_id"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("Severity") + .HasColumnType("INTEGER") + .HasColumnName("severity"); + + b.HasKey("Id") + .HasName("PK_server_role_ban"); + + b.HasIndex("Address"); + + b.HasIndex("BanningAdmin"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_server_role_ban_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_server_role_ban_round_id"); + + b.ToTable("server_role_ban", null, t => + { + t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR player_user_id IS NOT NULL OR hwid IS NOT NULL"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleUnban", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("role_unban_id"); + + b.Property("BanId") + .HasColumnType("INTEGER") + .HasColumnName("ban_id"); + + b.Property("UnbanTime") + .HasColumnType("TEXT") + .HasColumnName("unban_time"); + + b.Property("UnbanningAdmin") + .HasColumnType("TEXT") + .HasColumnName("unbanning_admin"); + + b.HasKey("Id") + .HasName("PK_server_role_unban"); + + b.HasIndex("BanId") + .IsUnique(); + + b.ToTable("server_role_unban", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerUnban", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("unban_id"); + + b.Property("BanId") + .HasColumnType("INTEGER") + .HasColumnName("ban_id"); + + b.Property("UnbanTime") + .HasColumnType("TEXT") + .HasColumnName("unban_time"); + + b.Property("UnbanningAdmin") + .HasColumnType("TEXT") + .HasColumnName("unbanning_admin"); + + b.HasKey("Id") + .HasName("PK_server_unban"); + + b.HasIndex("BanId") + .IsUnique(); + + b.ToTable("server_unban", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Trait", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("trait_id"); + + b.Property("ProfileId") + .HasColumnType("INTEGER") + .HasColumnName("profile_id"); + + b.Property("TraitName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("trait_name"); + + b.HasKey("Id") + .HasName("PK_trait"); + + b.HasIndex("ProfileId", "TraitName") + .IsUnique(); + + b.ToTable("trait", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.UploadedResourceLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("uploaded_resource_log_id"); + + b.Property("Data") + .IsRequired() + .HasColumnType("BLOB") + .HasColumnName("data"); + + b.Property("Date") + .HasColumnType("TEXT") + .HasColumnName("date"); + + b.Property("Path") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("path"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_uploaded_resource_log"); + + b.ToTable("uploaded_resource_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Whitelist", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.HasKey("UserId") + .HasName("PK_whitelist"); + + b.ToTable("whitelist", (string)null); + }); + + modelBuilder.Entity("PlayerRound", b => + { + b.Property("PlayersId") + .HasColumnType("INTEGER") + .HasColumnName("players_id"); + + b.Property("RoundsId") + .HasColumnType("INTEGER") + .HasColumnName("rounds_id"); + + b.HasKey("PlayersId", "RoundsId") + .HasName("PK_player_round"); + + b.HasIndex("RoundsId") + .HasDatabaseName("IX_player_round_rounds_id"); + + b.ToTable("player_round", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.HasOne("Content.Server.Database.AdminRank", "AdminRank") + .WithMany("Admins") + .HasForeignKey("AdminRankId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_admin_rank_admin_rank_id"); + + b.Navigation("AdminRank"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminFlag", b => + { + b.HasOne("Content.Server.Database.Admin", "Admin") + .WithMany("Flags") + .HasForeignKey("AdminId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_flag_admin_admin_id"); + + b.Navigation("Admin"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany("AdminLogs") + .HasForeignKey("RoundId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_round_round_id"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b => + { + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminLogs") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_player_player_player_user_id"); + + b.HasOne("Content.Server.Database.AdminLog", "Log") + .WithMany("Players") + .HasForeignKey("RoundId", "LogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_player_admin_log_round_id_log_id"); + + b.Navigation("Log"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminMessage", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminMessagesCreated") + .HasForeignKey("CreatedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_messages_player_created_by_id"); + + b.HasOne("Content.Server.Database.Player", "DeletedBy") + .WithMany("AdminMessagesDeleted") + .HasForeignKey("DeletedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_messages_player_deleted_by_id"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminMessagesLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_messages_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminMessagesReceived") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_admin_messages_player_player_user_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_admin_messages_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("DeletedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Player"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminNote", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminNotesCreated") + .HasForeignKey("CreatedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_notes_player_created_by_id"); + + b.HasOne("Content.Server.Database.Player", "DeletedBy") + .WithMany("AdminNotesDeleted") + .HasForeignKey("DeletedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_notes_player_deleted_by_id"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminNotesLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_notes_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminNotesReceived") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_admin_notes_player_player_user_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_admin_notes_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("DeletedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Player"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b => + { + b.HasOne("Content.Server.Database.AdminRank", "Rank") + .WithMany("Flags") + .HasForeignKey("AdminRankId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_rank_flag_admin_rank_admin_rank_id"); + + b.Navigation("Rank"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminWatchlist", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminWatchlistsCreated") + .HasForeignKey("CreatedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_watchlists_player_created_by_id"); + + b.HasOne("Content.Server.Database.Player", "DeletedBy") + .WithMany("AdminWatchlistsDeleted") + .HasForeignKey("DeletedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_watchlists_player_deleted_by_id"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminWatchlistsLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_watchlists_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminWatchlistsReceived") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_admin_watchlists_player_player_user_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_admin_watchlists_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("DeletedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Player"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.Antag", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Antags") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_antag_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.HasOne("Content.Server.Database.Server", "Server") + .WithMany("ConnectionLogs") + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.SetNull) + .IsRequired() + .HasConstraintName("FK_connection_log_server_server_id"); + + b.OwnsOne("Content.Server.Database.TypedHwid", "HWId", b1 => + { + b1.Property("ConnectionLogId") + .HasColumnType("INTEGER") + .HasColumnName("connection_log_id"); + + b1.Property("Hwid") + .IsRequired() + .HasColumnType("BLOB") + .HasColumnName("hwid"); + + b1.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0) + .HasColumnName("hwid_type"); + + b1.HasKey("ConnectionLogId"); + + b1.ToTable("connection_log"); + + b1.WithOwner() + .HasForeignKey("ConnectionLogId") + .HasConstraintName("FK_connection_log_connection_log_connection_log_id"); + }); + + b.Navigation("HWId"); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("Content.Server.Database.Job", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Jobs") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_job_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.Player", b => + { + b.OwnsOne("Content.Server.Database.TypedHwid", "LastSeenHWId", b1 => + { + b1.Property("PlayerId") + .HasColumnType("INTEGER") + .HasColumnName("player_id"); + + b1.Property("Hwid") + .IsRequired() + .HasColumnType("BLOB") + .HasColumnName("last_seen_hwid"); + + b1.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0) + .HasColumnName("last_seen_hwid_type"); + + b1.HasKey("PlayerId"); + + b1.ToTable("player"); + + b1.WithOwner() + .HasForeignKey("PlayerId") + .HasConstraintName("FK_player_player_player_id"); + }); + + b.Navigation("LastSeenHWId"); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.HasOne("Content.Server.Database.Preference", "Preference") + .WithMany("Profiles") + .HasForeignKey("PreferenceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_profile_preference_preference_id"); + + b.Navigation("Preference"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileLoadout", b => + { + b.HasOne("Content.Server.Database.ProfileLoadoutGroup", "ProfileLoadoutGroup") + .WithMany("Loadouts") + .HasForeignKey("ProfileLoadoutGroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_profile_loadout_profile_loadout_group_profile_loadout_group_id"); + + b.Navigation("ProfileLoadoutGroup"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileLoadoutGroup", b => + { + b.HasOne("Content.Server.Database.ProfileRoleLoadout", "ProfileRoleLoadout") + .WithMany("Groups") + .HasForeignKey("ProfileRoleLoadoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_profile_loadout_group_profile_role_loadout_profile_role_loadout_id"); + + b.Navigation("ProfileRoleLoadout"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileRoleLoadout", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Loadouts") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_profile_role_loadout_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.RoleWhitelist", b => + { + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("JobWhitelists") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_role_whitelists_player_player_user_id"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.HasOne("Content.Server.Database.Server", "Server") + .WithMany("Rounds") + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_round_server_server_id"); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminServerBansCreated") + .HasForeignKey("BanningAdmin") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_ban_player_banning_admin"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminServerBansLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_ban_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_server_ban_round_round_id"); + + b.OwnsOne("Content.Server.Database.TypedHwid", "HWId", b1 => + { + b1.Property("ServerBanId") + .HasColumnType("INTEGER") + .HasColumnName("server_ban_id"); + + b1.Property("Hwid") + .IsRequired() + .HasColumnType("BLOB") + .HasColumnName("hwid"); + + b1.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0) + .HasColumnName("hwid_type"); + + b1.HasKey("ServerBanId"); + + b1.ToTable("server_ban"); + + b1.WithOwner() + .HasForeignKey("ServerBanId") + .HasConstraintName("FK_server_ban_server_ban_server_ban_id"); + }); + + b.Navigation("CreatedBy"); + + b.Navigation("HWId"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanHit", b => + { + b.HasOne("Content.Server.Database.ServerBan", "Ban") + .WithMany("BanHits") + .HasForeignKey("BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_ban_hit_server_ban_ban_id"); + + b.HasOne("Content.Server.Database.ConnectionLog", "Connection") + .WithMany("BanHits") + .HasForeignKey("ConnectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_ban_hit_connection_log_connection_id"); + + b.Navigation("Ban"); + + b.Navigation("Connection"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminServerRoleBansCreated") + .HasForeignKey("BanningAdmin") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_role_ban_player_banning_admin"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminServerRoleBansLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_role_ban_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_server_role_ban_round_round_id"); + + b.OwnsOne("Content.Server.Database.TypedHwid", "HWId", b1 => + { + b1.Property("ServerRoleBanId") + .HasColumnType("INTEGER") + .HasColumnName("server_role_ban_id"); + + b1.Property("Hwid") + .IsRequired() + .HasColumnType("BLOB") + .HasColumnName("hwid"); + + b1.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0) + .HasColumnName("hwid_type"); + + b1.HasKey("ServerRoleBanId"); + + b1.ToTable("server_role_ban"); + + b1.WithOwner() + .HasForeignKey("ServerRoleBanId") + .HasConstraintName("FK_server_role_ban_server_role_ban_server_role_ban_id"); + }); + + b.Navigation("CreatedBy"); + + b.Navigation("HWId"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleUnban", b => + { + b.HasOne("Content.Server.Database.ServerRoleBan", "Ban") + .WithOne("Unban") + .HasForeignKey("Content.Server.Database.ServerRoleUnban", "BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_role_unban_server_role_ban_ban_id"); + + b.Navigation("Ban"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerUnban", b => + { + b.HasOne("Content.Server.Database.ServerBan", "Ban") + .WithOne("Unban") + .HasForeignKey("Content.Server.Database.ServerUnban", "BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_unban_server_ban_ban_id"); + + b.Navigation("Ban"); + }); + + modelBuilder.Entity("Content.Server.Database.Trait", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Traits") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_trait_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("PlayerRound", b => + { + b.HasOne("Content.Server.Database.Player", null) + .WithMany() + .HasForeignKey("PlayersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_player_round_player_players_id"); + + b.HasOne("Content.Server.Database.Round", null) + .WithMany() + .HasForeignKey("RoundsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_player_round_round_rounds_id"); + }); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.Navigation("Flags"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.Navigation("Players"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRank", b => + { + b.Navigation("Admins"); + + b.Navigation("Flags"); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.Navigation("BanHits"); + }); + + modelBuilder.Entity("Content.Server.Database.Player", b => + { + b.Navigation("AdminLogs"); + + b.Navigation("AdminMessagesCreated"); + + b.Navigation("AdminMessagesDeleted"); + + b.Navigation("AdminMessagesLastEdited"); + + b.Navigation("AdminMessagesReceived"); + + b.Navigation("AdminNotesCreated"); + + b.Navigation("AdminNotesDeleted"); + + b.Navigation("AdminNotesLastEdited"); + + b.Navigation("AdminNotesReceived"); + + b.Navigation("AdminServerBansCreated"); + + b.Navigation("AdminServerBansLastEdited"); + + b.Navigation("AdminServerRoleBansCreated"); + + b.Navigation("AdminServerRoleBansLastEdited"); + + b.Navigation("AdminWatchlistsCreated"); + + b.Navigation("AdminWatchlistsDeleted"); + + b.Navigation("AdminWatchlistsLastEdited"); + + b.Navigation("AdminWatchlistsReceived"); + + b.Navigation("JobWhitelists"); + }); + + modelBuilder.Entity("Content.Server.Database.Preference", b => + { + b.Navigation("Profiles"); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.Navigation("Antags"); + + b.Navigation("Jobs"); + + b.Navigation("Loadouts"); + + b.Navigation("Traits"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileLoadoutGroup", b => + { + b.Navigation("Loadouts"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileRoleLoadout", b => + { + b.Navigation("Groups"); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.Navigation("AdminLogs"); + }); + + modelBuilder.Entity("Content.Server.Database.Server", b => + { + b.Navigation("ConnectionLogs"); + + b.Navigation("Rounds"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.Navigation("BanHits"); + + b.Navigation("Unban"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.Navigation("Unban"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Content.Server.Database/Migrations/Sqlite/20241111170107_ModernHwid.cs b/Content.Server.Database/Migrations/Sqlite/20241111170107_ModernHwid.cs new file mode 100644 index 0000000000..97b5dafd03 --- /dev/null +++ b/Content.Server.Database/Migrations/Sqlite/20241111170107_ModernHwid.cs @@ -0,0 +1,62 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Content.Server.Database.Migrations.Sqlite +{ + /// + public partial class ModernHwid : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "hwid_type", + table: "server_role_ban", + type: "INTEGER", + nullable: true, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "hwid_type", + table: "server_ban", + type: "INTEGER", + nullable: true, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "last_seen_hwid_type", + table: "player", + type: "INTEGER", + nullable: true, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "hwid_type", + table: "connection_log", + type: "INTEGER", + nullable: true, + defaultValue: 0); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "hwid_type", + table: "server_role_ban"); + + migrationBuilder.DropColumn( + name: "hwid_type", + table: "server_ban"); + + migrationBuilder.DropColumn( + name: "last_seen_hwid_type", + table: "player"); + + migrationBuilder.DropColumn( + name: "hwid_type", + table: "connection_log"); + } + } +} diff --git a/Content.Server.Database/Migrations/Sqlite/20241111193602_ConnectionTrust.Designer.cs b/Content.Server.Database/Migrations/Sqlite/20241111193602_ConnectionTrust.Designer.cs new file mode 100644 index 0000000000..bd4e20a464 --- /dev/null +++ b/Content.Server.Database/Migrations/Sqlite/20241111193602_ConnectionTrust.Designer.cs @@ -0,0 +1,1999 @@ +// +using System; +using Content.Server.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Content.Server.Database.Migrations.Sqlite +{ + [DbContext(typeof(SqliteServerDbContext))] + [Migration("20241111193602_ConnectionTrust")] + partial class ConnectionTrust + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.0"); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.Property("AdminRankId") + .HasColumnType("INTEGER") + .HasColumnName("admin_rank_id"); + + b.Property("Title") + .HasColumnType("TEXT") + .HasColumnName("title"); + + b.HasKey("UserId") + .HasName("PK_admin"); + + b.HasIndex("AdminRankId") + .HasDatabaseName("IX_admin_admin_rank_id"); + + b.ToTable("admin", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminFlag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_flag_id"); + + b.Property("AdminId") + .HasColumnType("TEXT") + .HasColumnName("admin_id"); + + b.Property("Flag") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("flag"); + + b.Property("Negative") + .HasColumnType("INTEGER") + .HasColumnName("negative"); + + b.HasKey("Id") + .HasName("PK_admin_flag"); + + b.HasIndex("AdminId") + .HasDatabaseName("IX_admin_flag_admin_id"); + + b.HasIndex("Flag", "AdminId") + .IsUnique(); + + b.ToTable("admin_flag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("Id") + .HasColumnType("INTEGER") + .HasColumnName("admin_log_id"); + + b.Property("Date") + .HasColumnType("TEXT") + .HasColumnName("date"); + + b.Property("Impact") + .HasColumnType("INTEGER") + .HasColumnName("impact"); + + b.Property("Json") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("json"); + + b.Property("Message") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("message"); + + b.Property("Type") + .HasColumnType("INTEGER") + .HasColumnName("type"); + + b.HasKey("RoundId", "Id") + .HasName("PK_admin_log"); + + b.HasIndex("Date"); + + b.HasIndex("Type") + .HasDatabaseName("IX_admin_log_type"); + + b.ToTable("admin_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b => + { + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("LogId") + .HasColumnType("INTEGER") + .HasColumnName("log_id"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.HasKey("RoundId", "LogId", "PlayerUserId") + .HasName("PK_admin_log_player"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_log_player_player_user_id"); + + b.ToTable("admin_log_player", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_messages_id"); + + b.Property("CreatedAt") + .HasColumnType("TEXT") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("TEXT") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("INTEGER") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("TEXT") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("TEXT") + .HasColumnName("deleted_by_id"); + + b.Property("Dismissed") + .HasColumnType("INTEGER") + .HasColumnName("dismissed"); + + b.Property("ExpirationTime") + .HasColumnType("TEXT") + .HasColumnName("expiration_time"); + + b.Property("LastEditedAt") + .HasColumnType("TEXT") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("TEXT") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("TEXT") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("TEXT") + .HasColumnName("playtime_at_note"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("Seen") + .HasColumnType("INTEGER") + .HasColumnName("seen"); + + b.HasKey("Id") + .HasName("PK_admin_messages"); + + b.HasIndex("CreatedById"); + + b.HasIndex("DeletedById"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_messages_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_messages_round_id"); + + b.ToTable("admin_messages", null, t => + { + t.HasCheckConstraint("NotDismissedAndSeen", "NOT dismissed OR seen"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.AdminNote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_notes_id"); + + b.Property("CreatedAt") + .HasColumnType("TEXT") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("TEXT") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("INTEGER") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("TEXT") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("TEXT") + .HasColumnName("deleted_by_id"); + + b.Property("ExpirationTime") + .HasColumnType("TEXT") + .HasColumnName("expiration_time"); + + b.Property("LastEditedAt") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("TEXT") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("TEXT") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("TEXT") + .HasColumnName("playtime_at_note"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("Secret") + .HasColumnType("INTEGER") + .HasColumnName("secret"); + + b.Property("Severity") + .HasColumnType("INTEGER") + .HasColumnName("severity"); + + b.HasKey("Id") + .HasName("PK_admin_notes"); + + b.HasIndex("CreatedById"); + + b.HasIndex("DeletedById"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_notes_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_notes_round_id"); + + b.ToTable("admin_notes", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRank", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_rank_id"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("PK_admin_rank"); + + b.ToTable("admin_rank", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_rank_flag_id"); + + b.Property("AdminRankId") + .HasColumnType("INTEGER") + .HasColumnName("admin_rank_id"); + + b.Property("Flag") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("flag"); + + b.HasKey("Id") + .HasName("PK_admin_rank_flag"); + + b.HasIndex("AdminRankId"); + + b.HasIndex("Flag", "AdminRankId") + .IsUnique(); + + b.ToTable("admin_rank_flag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminWatchlist", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_watchlists_id"); + + b.Property("CreatedAt") + .HasColumnType("TEXT") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("TEXT") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("INTEGER") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("TEXT") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("TEXT") + .HasColumnName("deleted_by_id"); + + b.Property("ExpirationTime") + .HasColumnType("TEXT") + .HasColumnName("expiration_time"); + + b.Property("LastEditedAt") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("TEXT") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("TEXT") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("TEXT") + .HasColumnName("playtime_at_note"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.HasKey("Id") + .HasName("PK_admin_watchlists"); + + b.HasIndex("CreatedById"); + + b.HasIndex("DeletedById"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_watchlists_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_watchlists_round_id"); + + b.ToTable("admin_watchlists", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Antag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("antag_id"); + + b.Property("AntagName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("antag_name"); + + b.Property("ProfileId") + .HasColumnType("INTEGER") + .HasColumnName("profile_id"); + + b.HasKey("Id") + .HasName("PK_antag"); + + b.HasIndex("ProfileId", "AntagName") + .IsUnique(); + + b.ToTable("antag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AssignedUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("assigned_user_id_id"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("user_name"); + + b.HasKey("Id") + .HasName("PK_assigned_user_id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.HasIndex("UserName") + .IsUnique(); + + b.ToTable("assigned_user_id", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.BanTemplate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("ban_template_id"); + + b.Property("AutoDelete") + .HasColumnType("INTEGER") + .HasColumnName("auto_delete"); + + b.Property("ExemptFlags") + .HasColumnType("INTEGER") + .HasColumnName("exempt_flags"); + + b.Property("Hidden") + .HasColumnType("INTEGER") + .HasColumnName("hidden"); + + b.Property("Length") + .HasColumnType("TEXT") + .HasColumnName("length"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("reason"); + + b.Property("Severity") + .HasColumnType("INTEGER") + .HasColumnName("severity"); + + b.Property("Title") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("title"); + + b.HasKey("Id") + .HasName("PK_ban_template"); + + b.ToTable("ban_template", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Blacklist", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.HasKey("UserId") + .HasName("PK_blacklist"); + + b.ToTable("blacklist", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("connection_log_id"); + + b.Property("Address") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("address"); + + b.Property("Denied") + .HasColumnType("INTEGER") + .HasColumnName("denied"); + + b.Property("ServerId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0) + .HasColumnName("server_id"); + + b.Property("Time") + .HasColumnType("TEXT") + .HasColumnName("time"); + + b.Property("Trust") + .HasColumnType("REAL") + .HasColumnName("trust"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("user_name"); + + b.HasKey("Id") + .HasName("PK_connection_log"); + + b.HasIndex("ServerId") + .HasDatabaseName("IX_connection_log_server_id"); + + b.HasIndex("Time"); + + b.HasIndex("UserId"); + + b.ToTable("connection_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Job", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("job_id"); + + b.Property("JobName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("job_name"); + + b.Property("Priority") + .HasColumnType("INTEGER") + .HasColumnName("priority"); + + b.Property("ProfileId") + .HasColumnType("INTEGER") + .HasColumnName("profile_id"); + + b.HasKey("Id") + .HasName("PK_job"); + + b.HasIndex("ProfileId"); + + b.HasIndex("ProfileId", "JobName") + .IsUnique(); + + b.HasIndex(new[] { "ProfileId" }, "IX_job_one_high_priority") + .IsUnique() + .HasFilter("priority = 3"); + + b.ToTable("job", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.PlayTime", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("play_time_id"); + + b.Property("PlayerId") + .HasColumnType("TEXT") + .HasColumnName("player_id"); + + b.Property("TimeSpent") + .HasColumnType("TEXT") + .HasColumnName("time_spent"); + + b.Property("Tracker") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("tracker"); + + b.HasKey("Id") + .HasName("PK_play_time"); + + b.HasIndex("PlayerId", "Tracker") + .IsUnique(); + + b.ToTable("play_time", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("player_id"); + + b.Property("FirstSeenTime") + .HasColumnType("TEXT") + .HasColumnName("first_seen_time"); + + b.Property("LastReadRules") + .HasColumnType("TEXT") + .HasColumnName("last_read_rules"); + + b.Property("LastSeenAddress") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("last_seen_address"); + + b.Property("LastSeenTime") + .HasColumnType("TEXT") + .HasColumnName("last_seen_time"); + + b.Property("LastSeenUserName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("last_seen_user_name"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_player"); + + b.HasAlternateKey("UserId") + .HasName("ak_player_user_id"); + + b.HasIndex("LastSeenUserName"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("player", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Preference", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("preference_id"); + + b.Property("AdminOOCColor") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("admin_ooc_color"); + + b.Property("SelectedCharacterSlot") + .HasColumnType("INTEGER") + .HasColumnName("selected_character_slot"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_preference"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("preference", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("profile_id"); + + b.Property("Age") + .HasColumnType("INTEGER") + .HasColumnName("age"); + + b.Property("CharacterName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("char_name"); + + b.Property("EyeColor") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("eye_color"); + + b.Property("FacialHairColor") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("facial_hair_color"); + + b.Property("FacialHairName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("facial_hair_name"); + + b.Property("FlavorText") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("flavor_text"); + + b.Property("Gender") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("gender"); + + b.Property("HairColor") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("hair_color"); + + b.Property("HairName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("hair_name"); + + b.Property("Markings") + .HasColumnType("jsonb") + .HasColumnName("markings"); + + b.Property("PreferenceId") + .HasColumnType("INTEGER") + .HasColumnName("preference_id"); + + b.Property("PreferenceUnavailable") + .HasColumnType("INTEGER") + .HasColumnName("pref_unavailable"); + + b.Property("Sex") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("sex"); + + b.Property("SkinColor") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("skin_color"); + + b.Property("Slot") + .HasColumnType("INTEGER") + .HasColumnName("slot"); + + b.Property("SpawnPriority") + .HasColumnType("INTEGER") + .HasColumnName("spawn_priority"); + + b.Property("Species") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("species"); + + b.HasKey("Id") + .HasName("PK_profile"); + + b.HasIndex("PreferenceId") + .HasDatabaseName("IX_profile_preference_id"); + + b.HasIndex("Slot", "PreferenceId") + .IsUnique(); + + b.ToTable("profile", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileLoadout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("profile_loadout_id"); + + b.Property("LoadoutName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("loadout_name"); + + b.Property("ProfileLoadoutGroupId") + .HasColumnType("INTEGER") + .HasColumnName("profile_loadout_group_id"); + + b.HasKey("Id") + .HasName("PK_profile_loadout"); + + b.HasIndex("ProfileLoadoutGroupId"); + + b.ToTable("profile_loadout", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileLoadoutGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("profile_loadout_group_id"); + + b.Property("GroupName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("group_name"); + + b.Property("ProfileRoleLoadoutId") + .HasColumnType("INTEGER") + .HasColumnName("profile_role_loadout_id"); + + b.HasKey("Id") + .HasName("PK_profile_loadout_group"); + + b.HasIndex("ProfileRoleLoadoutId"); + + b.ToTable("profile_loadout_group", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileRoleLoadout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("profile_role_loadout_id"); + + b.Property("ProfileId") + .HasColumnType("INTEGER") + .HasColumnName("profile_id"); + + b.Property("RoleName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("role_name"); + + b.HasKey("Id") + .HasName("PK_profile_role_loadout"); + + b.HasIndex("ProfileId"); + + b.ToTable("profile_role_loadout", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.RoleWhitelist", b => + { + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("RoleId") + .HasColumnType("TEXT") + .HasColumnName("role_id"); + + b.HasKey("PlayerUserId", "RoleId") + .HasName("PK_role_whitelists"); + + b.ToTable("role_whitelists", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("ServerId") + .HasColumnType("INTEGER") + .HasColumnName("server_id"); + + b.Property("StartDate") + .HasColumnType("TEXT") + .HasColumnName("start_date"); + + b.HasKey("Id") + .HasName("PK_round"); + + b.HasIndex("ServerId") + .HasDatabaseName("IX_round_server_id"); + + b.HasIndex("StartDate"); + + b.ToTable("round", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Server", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("server_id"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("PK_server"); + + b.ToTable("server", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("server_ban_id"); + + b.Property("Address") + .HasColumnType("TEXT") + .HasColumnName("address"); + + b.Property("AutoDelete") + .HasColumnType("INTEGER") + .HasColumnName("auto_delete"); + + b.Property("BanTime") + .HasColumnType("TEXT") + .HasColumnName("ban_time"); + + b.Property("BanningAdmin") + .HasColumnType("TEXT") + .HasColumnName("banning_admin"); + + b.Property("ExemptFlags") + .HasColumnType("INTEGER") + .HasColumnName("exempt_flags"); + + b.Property("ExpirationTime") + .HasColumnType("TEXT") + .HasColumnName("expiration_time"); + + b.Property("Hidden") + .HasColumnType("INTEGER") + .HasColumnName("hidden"); + + b.Property("LastEditedAt") + .HasColumnType("TEXT") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("TEXT") + .HasColumnName("last_edited_by_id"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("TEXT") + .HasColumnName("playtime_at_note"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("reason"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("Severity") + .HasColumnType("INTEGER") + .HasColumnName("severity"); + + b.HasKey("Id") + .HasName("PK_server_ban"); + + b.HasIndex("Address"); + + b.HasIndex("BanningAdmin"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_server_ban_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_server_ban_round_id"); + + b.ToTable("server_ban", null, t => + { + t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR player_user_id IS NOT NULL OR hwid IS NOT NULL"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanExemption", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.Property("Flags") + .HasColumnType("INTEGER") + .HasColumnName("flags"); + + b.HasKey("UserId") + .HasName("PK_server_ban_exemption"); + + b.ToTable("server_ban_exemption", null, t => + { + t.HasCheckConstraint("FlagsNotZero", "flags != 0"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanHit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("server_ban_hit_id"); + + b.Property("BanId") + .HasColumnType("INTEGER") + .HasColumnName("ban_id"); + + b.Property("ConnectionId") + .HasColumnType("INTEGER") + .HasColumnName("connection_id"); + + b.HasKey("Id") + .HasName("PK_server_ban_hit"); + + b.HasIndex("BanId") + .HasDatabaseName("IX_server_ban_hit_ban_id"); + + b.HasIndex("ConnectionId") + .HasDatabaseName("IX_server_ban_hit_connection_id"); + + b.ToTable("server_ban_hit", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("server_role_ban_id"); + + b.Property("Address") + .HasColumnType("TEXT") + .HasColumnName("address"); + + b.Property("BanTime") + .HasColumnType("TEXT") + .HasColumnName("ban_time"); + + b.Property("BanningAdmin") + .HasColumnType("TEXT") + .HasColumnName("banning_admin"); + + b.Property("ExpirationTime") + .HasColumnType("TEXT") + .HasColumnName("expiration_time"); + + b.Property("Hidden") + .HasColumnType("INTEGER") + .HasColumnName("hidden"); + + b.Property("LastEditedAt") + .HasColumnType("TEXT") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("TEXT") + .HasColumnName("last_edited_by_id"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("TEXT") + .HasColumnName("playtime_at_note"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("reason"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("role_id"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("Severity") + .HasColumnType("INTEGER") + .HasColumnName("severity"); + + b.HasKey("Id") + .HasName("PK_server_role_ban"); + + b.HasIndex("Address"); + + b.HasIndex("BanningAdmin"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_server_role_ban_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_server_role_ban_round_id"); + + b.ToTable("server_role_ban", null, t => + { + t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR player_user_id IS NOT NULL OR hwid IS NOT NULL"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleUnban", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("role_unban_id"); + + b.Property("BanId") + .HasColumnType("INTEGER") + .HasColumnName("ban_id"); + + b.Property("UnbanTime") + .HasColumnType("TEXT") + .HasColumnName("unban_time"); + + b.Property("UnbanningAdmin") + .HasColumnType("TEXT") + .HasColumnName("unbanning_admin"); + + b.HasKey("Id") + .HasName("PK_server_role_unban"); + + b.HasIndex("BanId") + .IsUnique(); + + b.ToTable("server_role_unban", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerUnban", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("unban_id"); + + b.Property("BanId") + .HasColumnType("INTEGER") + .HasColumnName("ban_id"); + + b.Property("UnbanTime") + .HasColumnType("TEXT") + .HasColumnName("unban_time"); + + b.Property("UnbanningAdmin") + .HasColumnType("TEXT") + .HasColumnName("unbanning_admin"); + + b.HasKey("Id") + .HasName("PK_server_unban"); + + b.HasIndex("BanId") + .IsUnique(); + + b.ToTable("server_unban", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Trait", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("trait_id"); + + b.Property("ProfileId") + .HasColumnType("INTEGER") + .HasColumnName("profile_id"); + + b.Property("TraitName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("trait_name"); + + b.HasKey("Id") + .HasName("PK_trait"); + + b.HasIndex("ProfileId", "TraitName") + .IsUnique(); + + b.ToTable("trait", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.UploadedResourceLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("uploaded_resource_log_id"); + + b.Property("Data") + .IsRequired() + .HasColumnType("BLOB") + .HasColumnName("data"); + + b.Property("Date") + .HasColumnType("TEXT") + .HasColumnName("date"); + + b.Property("Path") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("path"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_uploaded_resource_log"); + + b.ToTable("uploaded_resource_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Whitelist", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.HasKey("UserId") + .HasName("PK_whitelist"); + + b.ToTable("whitelist", (string)null); + }); + + modelBuilder.Entity("PlayerRound", b => + { + b.Property("PlayersId") + .HasColumnType("INTEGER") + .HasColumnName("players_id"); + + b.Property("RoundsId") + .HasColumnType("INTEGER") + .HasColumnName("rounds_id"); + + b.HasKey("PlayersId", "RoundsId") + .HasName("PK_player_round"); + + b.HasIndex("RoundsId") + .HasDatabaseName("IX_player_round_rounds_id"); + + b.ToTable("player_round", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.HasOne("Content.Server.Database.AdminRank", "AdminRank") + .WithMany("Admins") + .HasForeignKey("AdminRankId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_admin_rank_admin_rank_id"); + + b.Navigation("AdminRank"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminFlag", b => + { + b.HasOne("Content.Server.Database.Admin", "Admin") + .WithMany("Flags") + .HasForeignKey("AdminId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_flag_admin_admin_id"); + + b.Navigation("Admin"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany("AdminLogs") + .HasForeignKey("RoundId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_round_round_id"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b => + { + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminLogs") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_player_player_player_user_id"); + + b.HasOne("Content.Server.Database.AdminLog", "Log") + .WithMany("Players") + .HasForeignKey("RoundId", "LogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_player_admin_log_round_id_log_id"); + + b.Navigation("Log"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminMessage", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminMessagesCreated") + .HasForeignKey("CreatedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_messages_player_created_by_id"); + + b.HasOne("Content.Server.Database.Player", "DeletedBy") + .WithMany("AdminMessagesDeleted") + .HasForeignKey("DeletedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_messages_player_deleted_by_id"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminMessagesLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_messages_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminMessagesReceived") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_admin_messages_player_player_user_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_admin_messages_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("DeletedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Player"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminNote", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminNotesCreated") + .HasForeignKey("CreatedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_notes_player_created_by_id"); + + b.HasOne("Content.Server.Database.Player", "DeletedBy") + .WithMany("AdminNotesDeleted") + .HasForeignKey("DeletedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_notes_player_deleted_by_id"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminNotesLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_notes_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminNotesReceived") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_admin_notes_player_player_user_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_admin_notes_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("DeletedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Player"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b => + { + b.HasOne("Content.Server.Database.AdminRank", "Rank") + .WithMany("Flags") + .HasForeignKey("AdminRankId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_rank_flag_admin_rank_admin_rank_id"); + + b.Navigation("Rank"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminWatchlist", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminWatchlistsCreated") + .HasForeignKey("CreatedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_watchlists_player_created_by_id"); + + b.HasOne("Content.Server.Database.Player", "DeletedBy") + .WithMany("AdminWatchlistsDeleted") + .HasForeignKey("DeletedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_watchlists_player_deleted_by_id"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminWatchlistsLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_watchlists_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminWatchlistsReceived") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_admin_watchlists_player_player_user_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_admin_watchlists_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("DeletedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Player"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.Antag", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Antags") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_antag_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.HasOne("Content.Server.Database.Server", "Server") + .WithMany("ConnectionLogs") + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.SetNull) + .IsRequired() + .HasConstraintName("FK_connection_log_server_server_id"); + + b.OwnsOne("Content.Server.Database.TypedHwid", "HWId", b1 => + { + b1.Property("ConnectionLogId") + .HasColumnType("INTEGER") + .HasColumnName("connection_log_id"); + + b1.Property("Hwid") + .IsRequired() + .HasColumnType("BLOB") + .HasColumnName("hwid"); + + b1.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0) + .HasColumnName("hwid_type"); + + b1.HasKey("ConnectionLogId"); + + b1.ToTable("connection_log"); + + b1.WithOwner() + .HasForeignKey("ConnectionLogId") + .HasConstraintName("FK_connection_log_connection_log_connection_log_id"); + }); + + b.Navigation("HWId"); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("Content.Server.Database.Job", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Jobs") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_job_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.Player", b => + { + b.OwnsOne("Content.Server.Database.TypedHwid", "LastSeenHWId", b1 => + { + b1.Property("PlayerId") + .HasColumnType("INTEGER") + .HasColumnName("player_id"); + + b1.Property("Hwid") + .IsRequired() + .HasColumnType("BLOB") + .HasColumnName("last_seen_hwid"); + + b1.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0) + .HasColumnName("last_seen_hwid_type"); + + b1.HasKey("PlayerId"); + + b1.ToTable("player"); + + b1.WithOwner() + .HasForeignKey("PlayerId") + .HasConstraintName("FK_player_player_player_id"); + }); + + b.Navigation("LastSeenHWId"); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.HasOne("Content.Server.Database.Preference", "Preference") + .WithMany("Profiles") + .HasForeignKey("PreferenceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_profile_preference_preference_id"); + + b.Navigation("Preference"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileLoadout", b => + { + b.HasOne("Content.Server.Database.ProfileLoadoutGroup", "ProfileLoadoutGroup") + .WithMany("Loadouts") + .HasForeignKey("ProfileLoadoutGroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_profile_loadout_profile_loadout_group_profile_loadout_group_id"); + + b.Navigation("ProfileLoadoutGroup"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileLoadoutGroup", b => + { + b.HasOne("Content.Server.Database.ProfileRoleLoadout", "ProfileRoleLoadout") + .WithMany("Groups") + .HasForeignKey("ProfileRoleLoadoutId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_profile_loadout_group_profile_role_loadout_profile_role_loadout_id"); + + b.Navigation("ProfileRoleLoadout"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileRoleLoadout", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Loadouts") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_profile_role_loadout_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.RoleWhitelist", b => + { + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("JobWhitelists") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_role_whitelists_player_player_user_id"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.HasOne("Content.Server.Database.Server", "Server") + .WithMany("Rounds") + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_round_server_server_id"); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminServerBansCreated") + .HasForeignKey("BanningAdmin") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_ban_player_banning_admin"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminServerBansLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_ban_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_server_ban_round_round_id"); + + b.OwnsOne("Content.Server.Database.TypedHwid", "HWId", b1 => + { + b1.Property("ServerBanId") + .HasColumnType("INTEGER") + .HasColumnName("server_ban_id"); + + b1.Property("Hwid") + .IsRequired() + .HasColumnType("BLOB") + .HasColumnName("hwid"); + + b1.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0) + .HasColumnName("hwid_type"); + + b1.HasKey("ServerBanId"); + + b1.ToTable("server_ban"); + + b1.WithOwner() + .HasForeignKey("ServerBanId") + .HasConstraintName("FK_server_ban_server_ban_server_ban_id"); + }); + + b.Navigation("CreatedBy"); + + b.Navigation("HWId"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanHit", b => + { + b.HasOne("Content.Server.Database.ServerBan", "Ban") + .WithMany("BanHits") + .HasForeignKey("BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_ban_hit_server_ban_ban_id"); + + b.HasOne("Content.Server.Database.ConnectionLog", "Connection") + .WithMany("BanHits") + .HasForeignKey("ConnectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_ban_hit_connection_log_connection_id"); + + b.Navigation("Ban"); + + b.Navigation("Connection"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminServerRoleBansCreated") + .HasForeignKey("BanningAdmin") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_role_ban_player_banning_admin"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminServerRoleBansLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_role_ban_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_server_role_ban_round_round_id"); + + b.OwnsOne("Content.Server.Database.TypedHwid", "HWId", b1 => + { + b1.Property("ServerRoleBanId") + .HasColumnType("INTEGER") + .HasColumnName("server_role_ban_id"); + + b1.Property("Hwid") + .IsRequired() + .HasColumnType("BLOB") + .HasColumnName("hwid"); + + b1.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0) + .HasColumnName("hwid_type"); + + b1.HasKey("ServerRoleBanId"); + + b1.ToTable("server_role_ban"); + + b1.WithOwner() + .HasForeignKey("ServerRoleBanId") + .HasConstraintName("FK_server_role_ban_server_role_ban_server_role_ban_id"); + }); + + b.Navigation("CreatedBy"); + + b.Navigation("HWId"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleUnban", b => + { + b.HasOne("Content.Server.Database.ServerRoleBan", "Ban") + .WithOne("Unban") + .HasForeignKey("Content.Server.Database.ServerRoleUnban", "BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_role_unban_server_role_ban_ban_id"); + + b.Navigation("Ban"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerUnban", b => + { + b.HasOne("Content.Server.Database.ServerBan", "Ban") + .WithOne("Unban") + .HasForeignKey("Content.Server.Database.ServerUnban", "BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_unban_server_ban_ban_id"); + + b.Navigation("Ban"); + }); + + modelBuilder.Entity("Content.Server.Database.Trait", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Traits") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_trait_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("PlayerRound", b => + { + b.HasOne("Content.Server.Database.Player", null) + .WithMany() + .HasForeignKey("PlayersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_player_round_player_players_id"); + + b.HasOne("Content.Server.Database.Round", null) + .WithMany() + .HasForeignKey("RoundsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_player_round_round_rounds_id"); + }); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.Navigation("Flags"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.Navigation("Players"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRank", b => + { + b.Navigation("Admins"); + + b.Navigation("Flags"); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.Navigation("BanHits"); + }); + + modelBuilder.Entity("Content.Server.Database.Player", b => + { + b.Navigation("AdminLogs"); + + b.Navigation("AdminMessagesCreated"); + + b.Navigation("AdminMessagesDeleted"); + + b.Navigation("AdminMessagesLastEdited"); + + b.Navigation("AdminMessagesReceived"); + + b.Navigation("AdminNotesCreated"); + + b.Navigation("AdminNotesDeleted"); + + b.Navigation("AdminNotesLastEdited"); + + b.Navigation("AdminNotesReceived"); + + b.Navigation("AdminServerBansCreated"); + + b.Navigation("AdminServerBansLastEdited"); + + b.Navigation("AdminServerRoleBansCreated"); + + b.Navigation("AdminServerRoleBansLastEdited"); + + b.Navigation("AdminWatchlistsCreated"); + + b.Navigation("AdminWatchlistsDeleted"); + + b.Navigation("AdminWatchlistsLastEdited"); + + b.Navigation("AdminWatchlistsReceived"); + + b.Navigation("JobWhitelists"); + }); + + modelBuilder.Entity("Content.Server.Database.Preference", b => + { + b.Navigation("Profiles"); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.Navigation("Antags"); + + b.Navigation("Jobs"); + + b.Navigation("Loadouts"); + + b.Navigation("Traits"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileLoadoutGroup", b => + { + b.Navigation("Loadouts"); + }); + + modelBuilder.Entity("Content.Server.Database.ProfileRoleLoadout", b => + { + b.Navigation("Groups"); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.Navigation("AdminLogs"); + }); + + modelBuilder.Entity("Content.Server.Database.Server", b => + { + b.Navigation("ConnectionLogs"); + + b.Navigation("Rounds"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.Navigation("BanHits"); + + b.Navigation("Unban"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.Navigation("Unban"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Content.Server.Database/Migrations/Sqlite/20241111193602_ConnectionTrust.cs b/Content.Server.Database/Migrations/Sqlite/20241111193602_ConnectionTrust.cs new file mode 100644 index 0000000000..3a7fd784e1 --- /dev/null +++ b/Content.Server.Database/Migrations/Sqlite/20241111193602_ConnectionTrust.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Content.Server.Database.Migrations.Sqlite +{ + /// + public partial class ConnectionTrust : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "trust", + table: "connection_log", + type: "REAL", + nullable: false, + defaultValue: 0f); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "trust", + table: "connection_log"); + } + } +} diff --git a/Content.Server.Database/Migrations/Sqlite/SqliteServerDbContextModelSnapshot.cs b/Content.Server.Database/Migrations/Sqlite/SqliteServerDbContextModelSnapshot.cs index a5baf9d04b..3b2b680ddf 100644 --- a/Content.Server.Database/Migrations/Sqlite/SqliteServerDbContextModelSnapshot.cs +++ b/Content.Server.Database/Migrations/Sqlite/SqliteServerDbContextModelSnapshot.cs @@ -526,6 +526,19 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("ban_template", (string)null); }); + modelBuilder.Entity("Content.Server.Database.Blacklist", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.HasKey("UserId") + .HasName("PK_blacklist"); + + b.ToTable("blacklist", (string)null); + }); + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => { b.Property("Id") @@ -542,10 +555,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("INTEGER") .HasColumnName("denied"); - b.Property("HWId") - .HasColumnType("BLOB") - .HasColumnName("hwid"); - b.Property("ServerId") .ValueGeneratedOnAdd() .HasColumnType("INTEGER") @@ -556,6 +565,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("TEXT") .HasColumnName("time"); + b.Property("Trust") + .HasColumnType("REAL") + .HasColumnName("trust"); + b.Property("UserId") .HasColumnType("TEXT") .HasColumnName("user_id"); @@ -703,10 +716,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("TEXT") .HasColumnName("last_seen_address"); - b.Property("LastSeenHWId") - .HasColumnType("BLOB") - .HasColumnName("last_seen_hwid"); - b.Property("LastSeenTime") .HasColumnType("TEXT") .HasColumnName("last_seen_time"); @@ -975,10 +984,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("TEXT") .HasColumnName("expiration_time"); - b.Property("HWId") - .HasColumnType("BLOB") - .HasColumnName("hwid"); - b.Property("Hidden") .HasColumnType("INTEGER") .HasColumnName("hidden"); @@ -1103,10 +1108,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("TEXT") .HasColumnName("expiration_time"); - b.Property("HWId") - .HasColumnType("BLOB") - .HasColumnName("hwid"); - b.Property("Hidden") .HasColumnType("INTEGER") .HasColumnName("hidden"); @@ -1538,6 +1539,34 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasConstraintName("FK_connection_log_server_server_id"); + b.OwnsOne("Content.Server.Database.TypedHwid", "HWId", b1 => + { + b1.Property("ConnectionLogId") + .HasColumnType("INTEGER") + .HasColumnName("connection_log_id"); + + b1.Property("Hwid") + .IsRequired() + .HasColumnType("BLOB") + .HasColumnName("hwid"); + + b1.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0) + .HasColumnName("hwid_type"); + + b1.HasKey("ConnectionLogId"); + + b1.ToTable("connection_log"); + + b1.WithOwner() + .HasForeignKey("ConnectionLogId") + .HasConstraintName("FK_connection_log_connection_log_connection_log_id"); + }); + + b.Navigation("HWId"); + b.Navigation("Server"); }); @@ -1565,6 +1594,37 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Profile"); }); + modelBuilder.Entity("Content.Server.Database.Player", b => + { + b.OwnsOne("Content.Server.Database.TypedHwid", "LastSeenHWId", b1 => + { + b1.Property("PlayerId") + .HasColumnType("INTEGER") + .HasColumnName("player_id"); + + b1.Property("Hwid") + .IsRequired() + .HasColumnType("BLOB") + .HasColumnName("last_seen_hwid"); + + b1.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0) + .HasColumnName("last_seen_hwid_type"); + + b1.HasKey("PlayerId"); + + b1.ToTable("player"); + + b1.WithOwner() + .HasForeignKey("PlayerId") + .HasConstraintName("FK_player_player_player_id"); + }); + + b.Navigation("LastSeenHWId"); + }); + modelBuilder.Entity("Content.Server.Database.Profile", b => { b.HasOne("Content.Server.Database.Preference", "Preference") @@ -1623,8 +1683,36 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasForeignKey("RoundId") .HasConstraintName("FK_server_ban_round_round_id"); + b.OwnsOne("Content.Server.Database.TypedHwid", "HWId", b1 => + { + b1.Property("ServerBanId") + .HasColumnType("INTEGER") + .HasColumnName("server_ban_id"); + + b1.Property("Hwid") + .IsRequired() + .HasColumnType("BLOB") + .HasColumnName("hwid"); + + b1.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0) + .HasColumnName("hwid_type"); + + b1.HasKey("ServerBanId"); + + b1.ToTable("server_ban"); + + b1.WithOwner() + .HasForeignKey("ServerBanId") + .HasConstraintName("FK_server_ban_server_ban_server_ban_id"); + }); + b.Navigation("CreatedBy"); + b.Navigation("HWId"); + b.Navigation("LastEditedBy"); b.Navigation("Round"); @@ -1672,8 +1760,36 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasForeignKey("RoundId") .HasConstraintName("FK_server_role_ban_round_round_id"); + b.OwnsOne("Content.Server.Database.TypedHwid", "HWId", b1 => + { + b1.Property("ServerRoleBanId") + .HasColumnType("INTEGER") + .HasColumnName("server_role_ban_id"); + + b1.Property("Hwid") + .IsRequired() + .HasColumnType("BLOB") + .HasColumnName("hwid"); + + b1.Property("Type") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0) + .HasColumnName("hwid_type"); + + b1.HasKey("ServerRoleBanId"); + + b1.ToTable("server_role_ban"); + + b1.WithOwner() + .HasForeignKey("ServerRoleBanId") + .HasConstraintName("FK_server_role_ban_server_role_ban_server_role_ban_id"); + }); + b.Navigation("CreatedBy"); + b.Navigation("HWId"); + b.Navigation("LastEditedBy"); b.Navigation("Round"); diff --git a/Content.Server.Database/Model.cs b/Content.Server.Database/Model.cs index 96f2aad380..377718bcba 100644 --- a/Content.Server.Database/Model.cs +++ b/Content.Server.Database/Model.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Net; using System.Text.Json; @@ -315,6 +317,47 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasForeignKey(w => w.PlayerUserId) .HasPrincipalKey(p => p.UserId) .OnDelete(DeleteBehavior.Cascade); + + // Changes for modern HWID integration + modelBuilder.Entity() + .OwnsOne(p => p.LastSeenHWId) + .Property(p => p.Hwid) + .HasColumnName("last_seen_hwid"); + + modelBuilder.Entity() + .OwnsOne(p => p.LastSeenHWId) + .Property(p => p.Type) + .HasDefaultValue(HwidType.Legacy); + + modelBuilder.Entity() + .OwnsOne(p => p.HWId) + .Property(p => p.Hwid) + .HasColumnName("hwid"); + + modelBuilder.Entity() + .OwnsOne(p => p.HWId) + .Property(p => p.Type) + .HasDefaultValue(HwidType.Legacy); + + modelBuilder.Entity() + .OwnsOne(p => p.HWId) + .Property(p => p.Hwid) + .HasColumnName("hwid"); + + modelBuilder.Entity() + .OwnsOne(p => p.HWId) + .Property(p => p.Type) + .HasDefaultValue(HwidType.Legacy); + + modelBuilder.Entity() + .OwnsOne(p => p.HWId) + .Property(p => p.Hwid) + .HasColumnName("hwid"); + + modelBuilder.Entity() + .OwnsOne(p => p.HWId) + .Property(p => p.Type) + .HasDefaultValue(HwidType.Legacy); } public virtual IQueryable SearchLogs(IQueryable query, string searchText) @@ -454,7 +497,7 @@ public class Player public string LastSeenUserName { get; set; } = null!; public DateTime LastSeenTime { get; set; } public IPAddress LastSeenAddress { get; set; } = null!; - public byte[]? LastSeenHWId { get; set; } + public TypedHwid? LastSeenHWId { get; set; } // Data that changes with each round public List Rounds { get; set; } = null!; @@ -603,7 +646,7 @@ public interface IBanCommon where TUnban : IUnbanCommon int Id { get; set; } Guid? PlayerUserId { get; set; } NpgsqlInet? Address { get; set; } - byte[]? HWId { get; set; } + TypedHwid? HWId { get; set; } DateTime BanTime { get; set; } DateTime? ExpirationTime { get; set; } string Reason { get; set; } @@ -688,7 +731,7 @@ public class ServerBan : IBanCommon /// /// Hardware ID of the banned player. /// - public byte[]? HWId { get; set; } + public TypedHwid? HWId { get; set; } /// /// The time when the ban was applied by an administrator. @@ -826,7 +869,7 @@ public class ConnectionLog public DateTime Time { get; set; } public IPAddress Address { get; set; } = null!; - public byte[]? HWId { get; set; } + public TypedHwid? HWId { get; set; } public ConnectionDenyReason? Denied { get; set; } @@ -843,6 +886,8 @@ public class ConnectionLog public List BanHits { get; set; } = null!; public Server Server { get; set; } = null!; + + public float Trust { get; set; } } public enum ConnectionDenyReason : byte @@ -880,7 +925,7 @@ public sealed class ServerRoleBan : IBanCommon public Guid? PlayerUserId { get; set; } [Required] public TimeSpan PlaytimeAtNote { get; set; } public NpgsqlInet? Address { get; set; } - public byte[]? HWId { get; set; } + public TypedHwid? HWId { get; set; } public DateTime BanTime { get; set; } @@ -1141,4 +1186,37 @@ public sealed class BanTemplate /// public bool Hidden { get; set; } } + + /// + /// A hardware ID value together with its . + /// + /// + [Owned] + public sealed class TypedHwid + { + public byte[] Hwid { get; set; } = default!; + public HwidType Type { get; set; } + + [return: NotNullIfNotNull(nameof(immutable))] + public static implicit operator TypedHwid?(ImmutableTypedHwid? immutable) + { + if (immutable == null) + return null; + + return new TypedHwid + { + Hwid = immutable.Hwid.ToArray(), + Type = immutable.Type, + }; + } + + [return: NotNullIfNotNull(nameof(hwid))] + public static implicit operator ImmutableTypedHwid?(TypedHwid? hwid) + { + if (hwid == null) + return null; + + return new ImmutableTypedHwid(hwid.Hwid.ToImmutableArray(), hwid.Type); + } + } } diff --git a/Content.Server/Administration/BanList/BanListEui.cs b/Content.Server/Administration/BanList/BanListEui.cs index 8ddc7459d7..2ca126bf16 100644 --- a/Content.Server/Administration/BanList/BanListEui.cs +++ b/Content.Server/Administration/BanList/BanListEui.cs @@ -54,7 +54,7 @@ private void OnPermsChanged(AdminPermsChangedEventArgs args) private async Task LoadBans(NetUserId userId) { - foreach (var ban in await _db.GetServerBansAsync(null, userId, null)) + foreach (var ban in await _db.GetServerBansAsync(null, userId, null, null)) { SharedServerUnban? unban = null; if (ban.Unban is { } unbanDef) @@ -74,7 +74,7 @@ private async Task LoadBans(NetUserId userId) ? (address.address.ToString(), address.cidrMask) : null; - hwid = ban.HWId == null ? null : Convert.ToBase64String(ban.HWId.Value.AsSpan()); + hwid = ban.HWId?.ToString(); } Bans.Add(new SharedServerBan( @@ -95,7 +95,7 @@ private async Task LoadBans(NetUserId userId) private async Task LoadRoleBans(NetUserId userId) { - foreach (var ban in await _db.GetServerRoleBansAsync(null, userId, null)) + foreach (var ban in await _db.GetServerRoleBansAsync(null, userId, null, null)) { SharedServerUnban? unban = null; if (ban.Unban is { } unbanDef) @@ -115,7 +115,7 @@ private async Task LoadRoleBans(NetUserId userId) ? (address.address.ToString(), address.cidrMask) : null; - hwid = ban.HWId == null ? null : Convert.ToBase64String(ban.HWId.Value.AsSpan()); + hwid = ban.HWId?.ToString(); } RoleBans.Add(new SharedServerRoleBan( ban.Id, diff --git a/Content.Server/Administration/BanPanelEui.cs b/Content.Server/Administration/BanPanelEui.cs index aa6bd8d4bf..3e33693208 100644 --- a/Content.Server/Administration/BanPanelEui.cs +++ b/Content.Server/Administration/BanPanelEui.cs @@ -1,4 +1,3 @@ -using System.Collections.Immutable; using System.Net; using System.Net.Sockets; using Content.Server.Administration.Managers; @@ -28,7 +27,7 @@ public sealed class BanPanelEui : BaseEui private NetUserId? PlayerId { get; set; } private string PlayerName { get; set; } = string.Empty; private IPAddress? LastAddress { get; set; } - private ImmutableArray? LastHwid { get; set; } + private ImmutableTypedHwid? LastHwid { get; set; } private const int Ipv4_CIDR = 32; private const int Ipv6_CIDR = 64; @@ -52,7 +51,7 @@ public override void HandleMessage(EuiMessageBase msg) switch (msg) { case BanPanelEuiStateMsg.CreateBanRequest r: - BanPlayer(r.Player, r.IpAddress, r.UseLastIp, r.Hwid?.ToImmutableArray(), r.UseLastHwid, r.Minutes, r.Severity, r.Reason, r.Roles, r.Erase); + BanPlayer(r.Player, r.IpAddress, r.UseLastIp, r.Hwid, r.UseLastHwid, r.Minutes, r.Severity, r.Reason, r.Roles, r.Erase); break; case BanPanelEuiStateMsg.GetPlayerInfoRequest r: ChangePlayer(r.PlayerUsername); @@ -60,7 +59,7 @@ public override void HandleMessage(EuiMessageBase msg) } } - private async void BanPlayer(string? target, string? ipAddressString, bool useLastIp, ImmutableArray? hwid, bool useLastHwid, uint minutes, NoteSeverity severity, string reason, IReadOnlyCollection? roles, bool erase) + private async void BanPlayer(string? target, string? ipAddressString, bool useLastIp, ImmutableTypedHwid? hwid, bool useLastHwid, uint minutes, NoteSeverity severity, string reason, IReadOnlyCollection? roles, bool erase) { if (!_admins.HasAdminFlag(Player, AdminFlags.Ban)) { @@ -157,7 +156,7 @@ public async void ChangePlayer(string playerNameOrId) ChangePlayer(located?.UserId, located?.Username ?? string.Empty, located?.LastAddress, located?.LastHWId); } - public void ChangePlayer(NetUserId? playerId, string playerName, IPAddress? lastAddress, ImmutableArray? lastHwid) + public void ChangePlayer(NetUserId? playerId, string playerName, IPAddress? lastAddress, ImmutableTypedHwid? lastHwid) { PlayerId = playerId; PlayerName = playerName; diff --git a/Content.Server/Administration/Commands/BanListCommand.cs b/Content.Server/Administration/Commands/BanListCommand.cs index a5bc97dce3..2f7093ae1d 100644 --- a/Content.Server/Administration/Commands/BanListCommand.cs +++ b/Content.Server/Administration/Commands/BanListCommand.cs @@ -38,7 +38,7 @@ public override async void Execute(IConsoleShell shell, string argStr, string[] if (shell.Player is not { } player) { - var bans = await _dbManager.GetServerBansAsync(data.LastAddress, data.UserId, data.LastHWId, false); + var bans = await _dbManager.GetServerBansAsync(data.LastAddress, data.UserId, data.LastLegacyHWId, data.LastModernHWIds, false); if (bans.Count == 0) { diff --git a/Content.Server/Administration/Commands/RoleBanListCommand.cs b/Content.Server/Administration/Commands/RoleBanListCommand.cs index aa416bc33f..c87a0b3b54 100644 --- a/Content.Server/Administration/Commands/RoleBanListCommand.cs +++ b/Content.Server/Administration/Commands/RoleBanListCommand.cs @@ -44,7 +44,7 @@ public async void Execute(IConsoleShell shell, string argStr, string[] args) var targetUid = located.UserId; var targetAddress = located.LastAddress; - var bans = await dbMan.GetServerRoleBansAsync(targetAddress, targetUid, targetHWid, includeUnbanned); + var bans = await dbMan.GetServerRoleBansAsync(targetAddress, targetUid, located.LastLegacyHWId, located.LastModernHWIds, includeUnbanned); if (bans.Count == 0) { diff --git a/Content.Server/Administration/Managers/BanManager.cs b/Content.Server/Administration/Managers/BanManager.cs index e04a6e3d35..d36bcd29d0 100644 --- a/Content.Server/Administration/Managers/BanManager.cs +++ b/Content.Server/Administration/Managers/BanManager.cs @@ -62,6 +62,15 @@ public void Initialize() private async Task CachePlayerData(ICommonSession player, CancellationToken cancel) { var flags = await _db.GetBanExemption(player.UserId, cancel); + + var netChannel = player.Channel; + ImmutableArray? hwId = netChannel.UserData.HWId.Length == 0 ? null : netChannel.UserData.HWId; + var modernHwids = netChannel.UserData.ModernHWIds; + var roleBans = await _db.GetServerRoleBansAsync(netChannel.RemoteEndPoint.Address, player.UserId, hwId, modernHwids, false); + + var userRoleBans = new List(); + foreach (var ban in roleBans) + userRoleBans.Add(ban); cancel.ThrowIfCancellationRequested(); _cachedBanExemptions[player] = flags; _cachedRoleBans[player] = userRoleBans; @@ -121,7 +130,7 @@ public void Restart() } #region Server Bans - public async void CreateServerBan(NetUserId? target, string? targetUsername, NetUserId? banningAdmin, (IPAddress, int)? addressRange, ImmutableArray? hwid, uint? minutes, NoteSeverity severity, string reason) + public async void CreateServerBan(NetUserId? target, string? targetUsername, NetUserId? banningAdmin, (IPAddress, int)? addressRange, ImmutableTypedHwid? hwid, uint? minutes, NoteSeverity severity, string reason) { DateTimeOffset? expires = null; if (minutes > 0) @@ -155,9 +164,7 @@ public async void CreateServerBan(NetUserId? target, string? targetUsername, Net var addressRangeString = addressRange != null ? $"{addressRange.Value.Item1}/{addressRange.Value.Item2}" : "null"; - var hwidString = hwid != null - ? string.Concat(hwid.Value.Select(x => x.ToString("x2"))) - : "null"; + var hwidString = hwid?.ToString() ?? "null"; var expiresString = expires == null ? Loc.GetString("server-ban-string-never") : $"{expires}"; var key = _cfg.GetCVar(CCVars.AdminShowPIIOnBan) ? "server-ban-string" : "server-ban-string-no-pii"; @@ -197,6 +204,7 @@ private bool BanMatchesPlayer(ICommonSession player, ServerBanDef ban) UserId = player.UserId, Address = player.Channel.RemoteEndPoint.Address, HWId = player.Channel.UserData.HWId, + ModernHWIds = player.Channel.UserData.ModernHWIds, // It's possible for the player to not have cached data loading yet due to coincidental timing. // If this is the case, we assume they have all flags to avoid false-positives. ExemptFlags = _cachedBanExemptions.GetValueOrDefault(player, ServerBanExemptFlags.All), @@ -217,7 +225,7 @@ private void KickForBanDef(ICommonSession player, ServerBanDef def) #region Job Bans // If you are trying to remove timeOfBan, please don't. It's there because the note system groups role bans by time, reason and banning admin. // Removing it will clutter the note list. Please also make sure that department bans are applied to roles with the same DateTimeOffset. - public async void CreateRoleBan(NetUserId? target, string? targetUsername, NetUserId? banningAdmin, (IPAddress, int)? addressRange, ImmutableArray? hwid, string role, uint? minutes, NoteSeverity severity, string reason, DateTimeOffset timeOfBan) + public async void CreateRoleBan(NetUserId? target, string? targetUsername, NetUserId? banningAdmin, (IPAddress, int)? addressRange, ImmutableTypedHwid? hwid, string role, uint? minutes, NoteSeverity severity, string reason, DateTimeOffset timeOfBan) { if (!_prototypeManager.TryIndex(role, out JobPrototype? _)) { diff --git a/Content.Server/Administration/Managers/IBanManager.cs b/Content.Server/Administration/Managers/IBanManager.cs index c11e310a82..fc192cc306 100644 --- a/Content.Server/Administration/Managers/IBanManager.cs +++ b/Content.Server/Administration/Managers/IBanManager.cs @@ -24,7 +24,7 @@ public interface IBanManager /// Number of minutes to ban for. 0 and null mean permanent /// Severity of the resulting ban note /// Reason for the ban - public void CreateServerBan(NetUserId? target, string? targetUsername, NetUserId? banningAdmin, (IPAddress, int)? addressRange, ImmutableArray? hwid, uint? minutes, NoteSeverity severity, string reason); + public void CreateServerBan(NetUserId? target, string? targetUsername, NetUserId? banningAdmin, (IPAddress, int)? addressRange, ImmutableTypedHwid? hwid, uint? minutes, NoteSeverity severity, string reason); public HashSet? GetRoleBans(NetUserId playerUserId); public HashSet>? GetJobBans(NetUserId playerUserId); @@ -37,7 +37,7 @@ public interface IBanManager /// Reason for the ban /// Number of minutes to ban for. 0 and null mean permanent /// Time when the ban was applied, used for grouping role bans - public void CreateRoleBan(NetUserId? target, string? targetUsername, NetUserId? banningAdmin, (IPAddress, int)? addressRange, ImmutableArray? hwid, string role, uint? minutes, NoteSeverity severity, string reason, DateTimeOffset timeOfBan); + public void CreateRoleBan(NetUserId? target, string? targetUsername, NetUserId? banningAdmin, (IPAddress, int)? addressRange, ImmutableTypedHwid? hwid, string role, uint? minutes, NoteSeverity severity, string reason, DateTimeOffset timeOfBan); /// /// Pardons a role ban for the specified target, username or GUID diff --git a/Content.Server/Administration/PlayerLocator.cs b/Content.Server/Administration/PlayerLocator.cs index 64a85f19ad..25cc771468 100644 --- a/Content.Server/Administration/PlayerLocator.cs +++ b/Content.Server/Administration/PlayerLocator.cs @@ -5,16 +5,42 @@ using System.Net.Http.Json; using System.Threading; using System.Threading.Tasks; +using Content.Server.Connection; using Content.Server.Database; +using Content.Shared.Database; using JetBrains.Annotations; using Robust.Server.Player; using Robust.Shared; using Robust.Shared.Configuration; using Robust.Shared.Network; +using Robust.Shared.Player; namespace Content.Server.Administration { - public sealed record LocatedPlayerData(NetUserId UserId, IPAddress? LastAddress, ImmutableArray? LastHWId, string Username); + /// + /// Contains data resolved via . + /// + /// The ID of the located user. + /// The last known IP address that the user connected with. + /// + /// The last known HWID that the user connected with. + /// This should be used for placing new records involving HWIDs, such as bans. + /// For looking up data based on HWID, use combined and . + /// + /// The last known username for the user connected with. + /// + /// The last known legacy HWID value this user connected with. Only use for old lookups! + /// + /// + /// The set of last known modern HWIDs the user connected with. + /// + public sealed record LocatedPlayerData( + NetUserId UserId, + IPAddress? LastAddress, + ImmutableTypedHwid? LastHWId, + string Username, + ImmutableArray? LastLegacyHWId, + ImmutableArray> LastModernHWIds); /// /// Utilities for finding user IDs that extend to more than the server database. @@ -67,63 +93,42 @@ public PlayerLocator() { // Check people currently on the server, the easiest case. if (_playerManager.TryGetSessionByUsername(playerName, out var session)) - { - var userId = session.UserId; - var address = session.Channel.RemoteEndPoint.Address; - var hwId = session.Channel.UserData.HWId; - return new LocatedPlayerData(userId, address, hwId, session.Name); - } + return ReturnForSession(session); // Check database for past players. var record = await _db.GetPlayerRecordByUserName(playerName, cancel); if (record != null) - return new LocatedPlayerData(record.UserId, record.LastSeenAddress, record.HWId, record.LastSeenUserName); + return ReturnForPlayerRecord(record); // If all else fails, ask the auth server. var authServer = _configurationManager.GetCVar(CVars.AuthServer); var requestUri = $"{authServer}api/query/name?name={WebUtility.UrlEncode(playerName)}"; using var resp = await _httpClient.GetAsync(requestUri, cancel); - if (resp.StatusCode == HttpStatusCode.NotFound) - return null; - - if (!resp.IsSuccessStatusCode) - { - _sawmill.Error("Auth server returned bad response {StatusCode}!", resp.StatusCode); - return null; - } - - var responseData = await resp.Content.ReadFromJsonAsync(cancellationToken: cancel); - - if (responseData == null) - { - _sawmill.Error("Auth server returned null response!"); - return null; - } - - return new LocatedPlayerData(new NetUserId(responseData.UserId), null, null, responseData.UserName); + return await HandleAuthServerResponse(resp, cancel); } public async Task LookupIdAsync(NetUserId userId, CancellationToken cancel = default) { // Check people currently on the server, the easiest case. if (_playerManager.TryGetSessionById(userId, out var session)) - { - var address = session.Channel.RemoteEndPoint.Address; - var hwId = session.Channel.UserData.HWId; - return new LocatedPlayerData(userId, address, hwId, session.Name); - } + return ReturnForSession(session); // Check database for past players. var record = await _db.GetPlayerRecordByUserId(userId, cancel); if (record != null) - return new LocatedPlayerData(record.UserId, record.LastSeenAddress, record.HWId, record.LastSeenUserName); + return ReturnForPlayerRecord(record); // If all else fails, ask the auth server. var authServer = _configurationManager.GetCVar(CVars.AuthServer); var requestUri = $"{authServer}api/query/userid?userid={WebUtility.UrlEncode(userId.UserId.ToString())}"; using var resp = await _httpClient.GetAsync(requestUri, cancel); + return await HandleAuthServerResponse(resp, cancel); + } + + private async Task HandleAuthServerResponse(HttpResponseMessage resp, CancellationToken cancel) + { if (resp.StatusCode == HttpStatusCode.NotFound) return null; @@ -134,14 +139,40 @@ public PlayerLocator() } var responseData = await resp.Content.ReadFromJsonAsync(cancellationToken: cancel); - if (responseData == null) { _sawmill.Error("Auth server returned null response!"); return null; } - return new LocatedPlayerData(new NetUserId(responseData.UserId), null, null, responseData.UserName); + return new LocatedPlayerData(new NetUserId(responseData.UserId), null, null, responseData.UserName, null, []); + } + + private static LocatedPlayerData ReturnForSession(ICommonSession session) + { + var userId = session.UserId; + var address = session.Channel.RemoteEndPoint.Address; + var hwId = session.Channel.UserData.GetModernHwid(); + return new LocatedPlayerData( + userId, + address, + hwId, + session.Name, + session.Channel.UserData.HWId, + session.Channel.UserData.ModernHWIds); + } + + private static LocatedPlayerData ReturnForPlayerRecord(PlayerRecord record) + { + var hwid = record.HWId; + + return new LocatedPlayerData( + record.UserId, + record.LastSeenAddress, + hwid, + record.LastSeenUserName, + hwid is { Type: HwidType.Legacy } ? hwid.Hwid : null, + hwid is { Type: HwidType.Modern } ? [hwid.Hwid] : []); } public async Task LookupIdByNameOrIdAsync(string playerName, CancellationToken cancel = default) diff --git a/Content.Server/Administration/PlayerPanelEui.cs b/Content.Server/Administration/PlayerPanelEui.cs index 4c0df80601..6c30488886 100644 --- a/Content.Server/Administration/PlayerPanelEui.cs +++ b/Content.Server/Administration/PlayerPanelEui.cs @@ -173,11 +173,11 @@ public async void SetPlayerState() { _whitelisted = await _db.GetWhitelistStatusAsync(_targetPlayer.UserId); // This won't get associated ip or hwid bans but they were not placed on this account anyways - _bans = (await _db.GetServerBansAsync(null, _targetPlayer.UserId, null)).Count; + _bans = (await _db.GetServerBansAsync(null, _targetPlayer.UserId, null, null)).Count; // Unfortunately role bans for departments and stuff are issued individually. This means that a single role ban can have many individual role bans internally // The only way to distinguish whether a role ban is the same is to compare the ban time. // This is horrible and I would love to just erase the database and start from scratch instead but that's what I can do for now. - _roleBans = (await _db.GetServerRoleBansAsync(null, _targetPlayer.UserId, null)).DistinctBy(rb => rb.BanTime).Count(); + _roleBans = (await _db.GetServerRoleBansAsync(null, _targetPlayer.UserId, null, null)).DistinctBy(rb => rb.BanTime).Count(); } else { diff --git a/Content.Server/Administration/Systems/BwoinkSystem.cs b/Content.Server/Administration/Systems/BwoinkSystem.cs index 21b5f6d301..220e79f3f3 100644 --- a/Content.Server/Administration/Systems/BwoinkSystem.cs +++ b/Content.Server/Administration/Systems/BwoinkSystem.cs @@ -173,7 +173,7 @@ private async void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs } // Check if the user has been banned - var ban = await _dbManager.GetServerBanAsync(null, e.Session.UserId, null); + var ban = await _dbManager.GetServerBanAsync(null, e.Session.UserId, null, null); if (ban != null) { var banMessage = Loc.GetString("bwoink-system-player-banned", ("banReason", ban.Reason)); diff --git a/Content.Server/Connection/ConnectionManager.cs b/Content.Server/Connection/ConnectionManager.cs index 5209605d6d..1f36ba63dd 100644 --- a/Content.Server/Connection/ConnectionManager.cs +++ b/Content.Server/Connection/ConnectionManager.cs @@ -118,11 +118,14 @@ private async Task NetMgrOnConnecting(NetConnectingArgs e) var serverId = (await _serverDbEntry.ServerEntity).Id; + var hwid = e.UserData.GetModernHwid(); + var trust = e.UserData.Trust; + if (deny != null) { var (reason, msg, banHits) = deny.Value; - var id = await _db.AddConnectionLogAsync(userId, e.UserName, addr, e.UserData.HWId, reason, serverId); + var id = await _db.AddConnectionLogAsync(userId, e.UserName, addr, hwid, trust, reason, serverId); if (banHits is { Count: > 0 }) await _db.AddServerBanHitsAsync(id, banHits); @@ -134,12 +137,12 @@ private async Task NetMgrOnConnecting(NetConnectingArgs e) } else { - await _db.AddConnectionLogAsync(userId, e.UserName, addr, e.UserData.HWId, null, serverId); + await _db.AddConnectionLogAsync(userId, e.UserName, addr, hwid, trust, null, serverId); if (!ServerPreferencesManager.ShouldStorePrefs(e.AuthType)) return; - await _db.UpdatePlayerRecordAsync(userId, e.UserName, addr, e.UserData.HWId); + await _db.UpdatePlayerRecordAsync(userId, e.UserName, addr, hwid); } } @@ -197,7 +200,9 @@ session.Status is SessionStatus.Connected or SessionStatus.InGame hwId = null; } - var bans = await _db.GetServerBansAsync(addr, userId, hwId, includeUnbanned: false); + var modernHwid = e.UserData.ModernHWIds; + + var bans = await _db.GetServerBansAsync(addr, userId, hwId, modernHwid, includeUnbanned: false); if (bans.Count > 0) { var firstBan = bans[0]; diff --git a/Content.Server/Connection/UserDataExt.cs b/Content.Server/Connection/UserDataExt.cs new file mode 100644 index 0000000000..a409f79a75 --- /dev/null +++ b/Content.Server/Connection/UserDataExt.cs @@ -0,0 +1,24 @@ +using Content.Shared.Database; +using Robust.Shared.Network; + +namespace Content.Server.Connection; + +/// +/// Helper functions for working with . +/// +public static class UserDataExt +{ + /// + /// Get the preferred HWID that should be used for new records related to a player. + /// + /// + /// Players can have zero or more HWIDs, but for logging things like connection logs we generally + /// only want a single one. This method returns a nullable method. + /// + public static ImmutableTypedHwid? GetModernHwid(this NetUserData userData) + { + return userData.ModernHWIds.Length == 0 + ? null + : new ImmutableTypedHwid(userData.ModernHWIds[0], HwidType.Modern); + } +} diff --git a/Content.Server/Damage/Systems/DamageOtherOnHitSystem.cs b/Content.Server/Damage/Systems/DamageOtherOnHitSystem.cs index 0dc9808dc1..2ffd66fe06 100644 --- a/Content.Server/Damage/Systems/DamageOtherOnHitSystem.cs +++ b/Content.Server/Damage/Systems/DamageOtherOnHitSystem.cs @@ -1,5 +1,4 @@ using Content.Shared.Camera; -using Content.Shared.CombatMode.Pacification; using Content.Shared.Damage; using Content.Shared.Damage.Components; using Content.Shared.Damage.Events; @@ -12,7 +11,6 @@ using Content.Shared.Popups; using Content.Shared.Throwing; using Content.Shared.Weapons.Melee; -using Content.Server.Weapons.Melee; using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Physics.Components; @@ -32,13 +30,13 @@ public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnBeforeThrow, after: [typeof(PacificationSystem)]); - SubscribeLocalEvent(OnDamageExamine, after: [typeof(MeleeWeaponSystem)]); + SubscribeLocalEvent(OnBeforeThrow); + SubscribeLocalEvent(OnDamageExamine); } private void OnBeforeThrow(EntityUid uid, StaminaComponent component, ref BeforeThrowEvent args) { - if (args.Cancelled || !TryComp(args.ItemUid, out var damage)) + if (!TryComp(args.ItemUid, out var damage)) return; if (component.CritThreshold - component.StaminaDamage <= damage.StaminaCost) diff --git a/Content.Server/Database/BanMatcher.cs b/Content.Server/Database/BanMatcher.cs index e58e5b0b5f..f477ccd822 100644 --- a/Content.Server/Database/BanMatcher.cs +++ b/Content.Server/Database/BanMatcher.cs @@ -1,6 +1,7 @@ using System.Collections.Immutable; using System.Net; using Content.Server.IP; +using Content.Shared.Database; using Robust.Shared.Network; namespace Content.Server.Database; @@ -52,9 +53,28 @@ public static bool BanMatches(ServerBanDef ban, in PlayerInfo player) return true; } - return player.HWId is { Length: > 0 } hwIdVar - && ban.HWId != null - && hwIdVar.AsSpan().SequenceEqual(ban.HWId.Value.AsSpan()); + switch (ban.HWId?.Type) + { + case HwidType.Legacy: + if (player.HWId is { Length: > 0 } hwIdVar + && hwIdVar.AsSpan().SequenceEqual(ban.HWId.Hwid.AsSpan())) + { + return true; + } + break; + case HwidType.Modern: + if (player.ModernHWIds is { Length: > 0 } modernHwIdVar) + { + foreach (var hwid in modernHwIdVar) + { + if (hwid.AsSpan().SequenceEqual(ban.HWId.Hwid.AsSpan())) + return true; + } + } + break; + } + + return false; } /// @@ -73,10 +93,15 @@ public struct PlayerInfo public IPAddress? Address; /// - /// The hardware ID of the player. + /// The LEGACY hardware ID of the player. Corresponds with . /// public ImmutableArray? HWId; + /// + /// The modern hardware IDs of the player. Corresponds with . + /// + public ImmutableArray>? ModernHWIds; + /// /// Exemption flags the player has been granted. /// diff --git a/Content.Server/Database/DatabaseRecords.cs b/Content.Server/Database/DatabaseRecords.cs index c0d81147bb..30fba3434b 100644 --- a/Content.Server/Database/DatabaseRecords.cs +++ b/Content.Server/Database/DatabaseRecords.cs @@ -1,4 +1,3 @@ -using System.Collections.Immutable; using System.Net; using Content.Shared.Database; using Robust.Shared.Network; @@ -121,7 +120,7 @@ public sealed record PlayerRecord( string LastSeenUserName, DateTimeOffset LastSeenTime, IPAddress LastSeenAddress, - ImmutableArray? HWId); + ImmutableTypedHwid? HWId); public sealed record RoundRecord(int Id, DateTimeOffset? StartDate, ServerRecord Server); diff --git a/Content.Server/Database/ServerBanDef.cs b/Content.Server/Database/ServerBanDef.cs index 09a960e9a6..a09f9e959c 100644 --- a/Content.Server/Database/ServerBanDef.cs +++ b/Content.Server/Database/ServerBanDef.cs @@ -1,4 +1,3 @@ -using System.Collections.Immutable; using System.Net; using Content.Shared.CCVar; using Content.Shared.Database; @@ -13,7 +12,7 @@ public sealed class ServerBanDef public int? Id { get; } public NetUserId? UserId { get; } public (IPAddress address, int cidrMask)? Address { get; } - public ImmutableArray? HWId { get; } + public ImmutableTypedHwid? HWId { get; } public DateTimeOffset BanTime { get; } public DateTimeOffset? ExpirationTime { get; } @@ -28,7 +27,7 @@ public sealed class ServerBanDef public ServerBanDef(int? id, NetUserId? userId, (IPAddress, int)? address, - ImmutableArray? hwId, + TypedHwid? hwId, DateTimeOffset banTime, DateTimeOffset? expirationTime, int? roundId, diff --git a/Content.Server/Database/ServerDbBase.cs b/Content.Server/Database/ServerDbBase.cs index 876a20ff79..5f9ef727d6 100644 --- a/Content.Server/Database/ServerDbBase.cs +++ b/Content.Server/Database/ServerDbBase.cs @@ -355,12 +355,14 @@ public async Task AssignUserIdAsync(string name, NetUserId netUserId) /// /// The ip address of the user. /// The id of the user. - /// The HWId of the user. + /// The legacy HWId of the user. + /// The modern HWIDs of the user. /// The user's latest received un-pardoned ban, or null if none exist. public abstract Task GetServerBanAsync( IPAddress? address, NetUserId? userId, - ImmutableArray? hwId); + ImmutableArray? hwId, + ImmutableArray>? modernHWIds); /// /// Looks up an user's ban history. @@ -369,13 +371,15 @@ public async Task AssignUserIdAsync(string name, NetUserId netUserId) /// /// The ip address of the user. /// The id of the user. - /// The HWId of the user. + /// The legacy HWId of the user. + /// The modern HWIDs of the user. /// Include pardoned and expired bans. /// The user's ban history. public abstract Task> GetServerBansAsync( IPAddress? address, NetUserId? userId, ImmutableArray? hwId, + ImmutableArray>? modernHWIds, bool includeUnbanned); public abstract Task AddServerBanAsync(ServerBanDef serverBan); @@ -466,11 +470,13 @@ public async Task GetBanExemption(NetUserId userId, Cancel /// The IP address of the user. /// The NetUserId of the user. /// The Hardware Id of the user. + /// The modern HWIDs of the user. /// Whether expired and pardoned bans are included. /// The user's role ban history. public abstract Task> GetServerRoleBansAsync(IPAddress? address, NetUserId? userId, ImmutableArray? hwId, + ImmutableArray>? modernHWIds, bool includeUnbanned); public abstract Task AddServerRoleBanAsync(ServerRoleBanDef serverRoleBan); @@ -553,7 +559,7 @@ public async Task UpdatePlayerRecord( NetUserId userId, string userName, IPAddress address, - ImmutableArray hwId) + ImmutableTypedHwid? hwId) { await using var db = await GetDb(); @@ -570,7 +576,7 @@ public async Task UpdatePlayerRecord( record.LastSeenTime = DateTime.UtcNow; record.LastSeenAddress = address; record.LastSeenUserName = userName; - record.LastSeenHWId = hwId.ToArray(); + record.LastSeenHWId = hwId; await db.DbContext.SaveChangesAsync(); } @@ -616,7 +622,7 @@ protected async Task PlayerRecordExists(DbGuard db, NetUserId userId) player.LastSeenUserName, new DateTimeOffset(NormalizeDatabaseTime(player.LastSeenTime)), player.LastSeenAddress, - player.LastSeenHWId?.ToImmutableArray()); + player.LastSeenHWId); } #endregion @@ -625,11 +631,11 @@ protected async Task PlayerRecordExists(DbGuard db, NetUserId userId) /* * CONNECTION LOG */ - public abstract Task AddConnectionLogAsync( - NetUserId userId, + public abstract Task AddConnectionLogAsync(NetUserId userId, string userName, IPAddress address, - ImmutableArray hwId, + ImmutableTypedHwid? hwId, + float trust, ConnectionDenyReason? denied, int serverId); diff --git a/Content.Server/Database/ServerDbManager.cs b/Content.Server/Database/ServerDbManager.cs index c74b1e632c..c614e231cc 100644 --- a/Content.Server/Database/ServerDbManager.cs +++ b/Content.Server/Database/ServerDbManager.cs @@ -69,12 +69,14 @@ Task InitPrefsAsync( /// /// The ip address of the user. /// The id of the user. - /// The hardware ID of the user. + /// The legacy HWID of the user. + /// The modern HWIDs of the user. /// The user's latest received un-pardoned ban, or null if none exist. Task GetServerBanAsync( IPAddress? address, NetUserId? userId, - ImmutableArray? hwId); + ImmutableArray? hwId, + ImmutableArray>? modernHWIds); /// /// Looks up an user's ban history. @@ -82,14 +84,16 @@ Task InitPrefsAsync( /// /// The ip address of the user. /// The id of the user. - /// The HWId of the user. + /// The legacy HWId of the user. + /// The modern HWIDs of the user. /// If true, bans that have been expired or pardoned are also included. /// The user's ban history. Task> GetServerBansAsync( IPAddress? address, NetUserId? userId, ImmutableArray? hwId, - bool includeUnbanned = true); + ImmutableArray>? modernHWIds, + bool includeUnbanned=true); Task AddServerBanAsync(ServerBanDef serverBan); Task AddServerUnbanAsync(ServerUnbanDef serverBan); @@ -137,12 +141,14 @@ public Task EditServerBan( /// The IP address of the user. /// The NetUserId of the user. /// The Hardware Id of the user. + /// The modern HWIDs of the user. /// Whether expired and pardoned bans are included. /// The user's role ban history. Task> GetServerRoleBansAsync( IPAddress? address, NetUserId? userId, ImmutableArray? hwId, + ImmutableArray>? modernHWIds, bool includeUnbanned = true); Task AddServerRoleBanAsync(ServerRoleBanDef serverBan); @@ -180,7 +186,7 @@ Task UpdatePlayerRecordAsync( NetUserId userId, string userName, IPAddress address, - ImmutableArray hwId); + ImmutableTypedHwid? hwId); Task GetPlayerRecordByUserName(string userName, CancellationToken cancel = default); Task GetPlayerRecordByUserId(NetUserId userId, CancellationToken cancel = default); #endregion @@ -191,7 +197,8 @@ Task AddConnectionLogAsync( NetUserId userId, string userName, IPAddress address, - ImmutableArray hwId, + ImmutableTypedHwid? hwId, + float trust, ConnectionDenyReason? denied, int serverId); @@ -480,20 +487,22 @@ public Task AssignUserIdAsync(string name, NetUserId userId) public Task GetServerBanAsync( IPAddress? address, NetUserId? userId, - ImmutableArray? hwId) + ImmutableArray? hwId, + ImmutableArray>? modernHWIds) { DbReadOpsMetric.Inc(); - return RunDbCommand(() => _db.GetServerBanAsync(address, userId, hwId)); + return RunDbCommand(() => _db.GetServerBanAsync(address, userId, hwId, modernHWIds)); } public Task> GetServerBansAsync( IPAddress? address, NetUserId? userId, ImmutableArray? hwId, - bool includeUnbanned = true) + ImmutableArray>? modernHWIds, + bool includeUnbanned=true) { DbReadOpsMetric.Inc(); - return RunDbCommand(() => _db.GetServerBansAsync(address, userId, hwId, includeUnbanned)); + return RunDbCommand(() => _db.GetServerBansAsync(address, userId, hwId, modernHWIds, includeUnbanned)); } public Task AddServerBanAsync(ServerBanDef serverBan) @@ -537,10 +546,11 @@ public Task> GetServerRoleBansAsync( IPAddress? address, NetUserId? userId, ImmutableArray? hwId, + ImmutableArray>? modernHWIds, bool includeUnbanned = true) { DbReadOpsMetric.Inc(); - return RunDbCommand(() => _db.GetServerRoleBansAsync(address, userId, hwId, includeUnbanned)); + return RunDbCommand(() => _db.GetServerRoleBansAsync(address, userId, hwId, modernHWIds, includeUnbanned)); } public Task AddServerRoleBanAsync(ServerRoleBanDef serverRoleBan) @@ -582,7 +592,7 @@ public Task UpdatePlayerRecordAsync( NetUserId userId, string userName, IPAddress address, - ImmutableArray hwId) + ImmutableTypedHwid? hwId) { DbWriteOpsMetric.Inc(); return RunDbCommand(() => _db.UpdatePlayerRecord(userId, userName, address, hwId)); @@ -604,12 +614,13 @@ public Task AddConnectionLogAsync( NetUserId userId, string userName, IPAddress address, - ImmutableArray hwId, + ImmutableTypedHwid? hwId, + float trust, ConnectionDenyReason? denied, int serverId) { DbWriteOpsMetric.Inc(); - return RunDbCommand(() => _db.AddConnectionLogAsync(userId, userName, address, hwId, denied, serverId)); + return RunDbCommand(() => _db.AddConnectionLogAsync(userId, userName, address, hwId, trust, denied, serverId)); } public Task AddServerBanHitsAsync(int connection, IEnumerable bans) diff --git a/Content.Server/Database/ServerDbPostgres.cs b/Content.Server/Database/ServerDbPostgres.cs index 7d131f70dc..c034670837 100644 --- a/Content.Server/Database/ServerDbPostgres.cs +++ b/Content.Server/Database/ServerDbPostgres.cs @@ -9,6 +9,7 @@ using Content.Server.Administration.Logs; using Content.Server.IP; using Content.Shared.CCVar; +using Content.Shared.Database; using Microsoft.EntityFrameworkCore; using Robust.Shared.Configuration; using Robust.Shared.Network; @@ -73,7 +74,8 @@ public ServerDbPostgres(DbContextOptions options, public override async Task GetServerBanAsync( IPAddress? address, NetUserId? userId, - ImmutableArray? hwId) + ImmutableArray? hwId, + ImmutableArray>? modernHWIds) { if (address == null && userId == null && hwId == null) { @@ -84,7 +86,7 @@ public ServerDbPostgres(DbContextOptions options, var exempt = await GetBanExemptionCore(db, userId); var newPlayer = userId == null || !await PlayerRecordExists(db, userId.Value); - var query = MakeBanLookupQuery(address, userId, hwId, db, includeUnbanned: false, exempt, newPlayer) + var query = MakeBanLookupQuery(address, userId, hwId, modernHWIds, db, includeUnbanned: false, exempt, newPlayer) .OrderByDescending(b => b.BanTime); var ban = await query.FirstOrDefaultAsync(); @@ -94,7 +96,9 @@ public ServerDbPostgres(DbContextOptions options, public override async Task> GetServerBansAsync(IPAddress? address, NetUserId? userId, - ImmutableArray? hwId, bool includeUnbanned) + ImmutableArray? hwId, + ImmutableArray>? modernHWIds, + bool includeUnbanned) { if (address == null && userId == null && hwId == null) { @@ -105,7 +109,7 @@ public override async Task> GetServerBansAsync(IPAddress? add var exempt = await GetBanExemptionCore(db, userId); var newPlayer = !await db.PgDbContext.Player.AnyAsync(p => p.UserId == userId); - var query = MakeBanLookupQuery(address, userId, hwId, db, includeUnbanned, exempt, newPlayer); + var query = MakeBanLookupQuery(address, userId, hwId, modernHWIds, db, includeUnbanned, exempt, newPlayer); var queryBans = await query.ToArrayAsync(); var bans = new List(queryBans.Length); @@ -127,6 +131,7 @@ private static IQueryable MakeBanLookupQuery( IPAddress? address, NetUserId? userId, ImmutableArray? hwId, + ImmutableArray>? modernHWIds, DbGuardImpl db, bool includeUnbanned, ServerBanExemptFlags? exemptFlags, @@ -134,16 +139,11 @@ private static IQueryable MakeBanLookupQuery( { DebugTools.Assert(!(address == null && userId == null && hwId == null)); - IQueryable? query = null; - - if (userId is { } uid) - { - var newQ = db.PgDbContext.Ban - .Include(p => p.Unban) - .Where(b => b.PlayerUserId == uid.UserId); - - query = query == null ? newQ : query.Union(newQ); - } + var query = MakeBanLookupQualityShared( + userId, + hwId, + modernHWIds, + db.PgDbContext.Ban); if (address != null && !exemptFlags.GetValueOrDefault(ServerBanExemptFlags.None).HasFlag(ServerBanExemptFlags.IP)) { @@ -156,15 +156,6 @@ private static IQueryable MakeBanLookupQuery( query = query == null ? newQ : query.Union(newQ); } - if (hwId != null && hwId.Value.Length > 0) - { - var newQ = db.PgDbContext.Ban - .Include(p => p.Unban) - .Where(b => b.HWId!.SequenceEqual(hwId.Value.ToArray())); - - query = query == null ? newQ : query.Union(newQ); - } - DebugTools.Assert( query != null, "At least one filter item (IP/UserID/HWID) must have been given to make query not null."); @@ -186,6 +177,49 @@ private static IQueryable MakeBanLookupQuery( return query.Distinct(); } + private static IQueryable? MakeBanLookupQualityShared( + NetUserId? userId, + ImmutableArray? hwId, + ImmutableArray>? modernHWIds, + DbSet set) + where TBan : class, IBanCommon + where TUnban : class, IUnbanCommon + { + IQueryable? query = null; + + if (userId is { } uid) + { + var newQ = set + .Include(p => p.Unban) + .Where(b => b.PlayerUserId == uid.UserId); + + query = query == null ? newQ : query.Union(newQ); + } + + if (hwId != null && hwId.Value.Length > 0) + { + var newQ = set + .Include(p => p.Unban) + .Where(b => b.HWId!.Type == HwidType.Legacy && b.HWId!.Hwid.SequenceEqual(hwId.Value.ToArray())); + + query = query == null ? newQ : query.Union(newQ); + } + + if (modernHWIds != null) + { + foreach (var modernHwid in modernHWIds) + { + var newQ = set + .Include(p => p.Unban) + .Where(b => b.HWId!.Type == HwidType.Modern && b.HWId!.Hwid.SequenceEqual(modernHwid.ToArray())); + + query = query == null ? newQ : query.Union(newQ); + } + } + + return query; + } + private static ServerBanDef? ConvertBan(ServerBan? ban) { if (ban == null) @@ -211,7 +245,7 @@ private static IQueryable MakeBanLookupQuery( ban.Id, uid, ban.Address.ToTuple(), - ban.HWId == null ? null : ImmutableArray.Create(ban.HWId), + ban.HWId, ban.BanTime, ban.ExpirationTime, ban.RoundId, @@ -249,7 +283,7 @@ public override async Task AddServerBanAsync(ServerBanDef serverBan) db.PgDbContext.Ban.Add(new ServerBan { Address = serverBan.Address.ToNpgsqlInet(), - HWId = serverBan.HWId?.ToArray(), + HWId = serverBan.HWId, Reason = serverBan.Reason, Severity = serverBan.Severity, BanningAdmin = serverBan.BanningAdmin?.UserId, @@ -297,6 +331,7 @@ public override async Task AddServerUnbanAsync(ServerUnbanDef serverUnban) public override async Task> GetServerRoleBansAsync(IPAddress? address, NetUserId? userId, ImmutableArray? hwId, + ImmutableArray>? modernHWIds, bool includeUnbanned) { if (address == null && userId == null && hwId == null) @@ -306,7 +341,7 @@ public override async Task> GetServerRoleBansAsync(IPAddr await using var db = await GetDbImpl(); - var query = MakeRoleBanLookupQuery(address, userId, hwId, db, includeUnbanned) + var query = MakeRoleBanLookupQuery(address, userId, hwId, modernHWIds, db, includeUnbanned) .OrderByDescending(b => b.BanTime); return await QueryRoleBans(query); @@ -334,19 +369,15 @@ private static IQueryable MakeRoleBanLookupQuery( IPAddress? address, NetUserId? userId, ImmutableArray? hwId, + ImmutableArray>? modernHWIds, DbGuardImpl db, bool includeUnbanned) { - IQueryable? query = null; - - if (userId is { } uid) - { - var newQ = db.PgDbContext.RoleBan - .Include(p => p.Unban) - .Where(b => b.PlayerUserId == uid.UserId); - - query = query == null ? newQ : query.Union(newQ); - } + var query = MakeBanLookupQualityShared( + userId, + hwId, + modernHWIds, + db.PgDbContext.RoleBan); if (address != null) { @@ -357,15 +388,6 @@ private static IQueryable MakeRoleBanLookupQuery( query = query == null ? newQ : query.Union(newQ); } - if (hwId != null && hwId.Value.Length > 0) - { - var newQ = db.PgDbContext.RoleBan - .Include(p => p.Unban) - .Where(b => b.HWId!.SequenceEqual(hwId.Value.ToArray())); - - query = query == null ? newQ : query.Union(newQ); - } - if (!includeUnbanned) { query = query?.Where(p => @@ -402,7 +424,7 @@ private static IQueryable MakeRoleBanLookupQuery( ban.Id, uid, ban.Address.ToTuple(), - ban.HWId == null ? null : ImmutableArray.Create(ban.HWId), + ban.HWId, ban.BanTime, ban.ExpirationTime, ban.RoundId, @@ -440,7 +462,7 @@ public override async Task AddServerRoleBanAsync(ServerRoleBan var ban = new ServerRoleBan { Address = serverRoleBan.Address.ToNpgsqlInet(), - HWId = serverRoleBan.HWId?.ToArray(), + HWId = serverRoleBan.HWId, Reason = serverRoleBan.Reason, Severity = serverRoleBan.Severity, BanningAdmin = serverRoleBan.BanningAdmin?.UserId, @@ -476,7 +498,8 @@ public override async Task AddConnectionLogAsync( NetUserId userId, string userName, IPAddress address, - ImmutableArray hwId, + ImmutableTypedHwid? hwId, + float trust, ConnectionDenyReason? denied, int serverId) { @@ -488,9 +511,10 @@ public override async Task AddConnectionLogAsync( Time = DateTime.UtcNow, UserId = userId.UserId, UserName = userName, - HWId = hwId.ToArray(), + HWId = hwId, Denied = denied, - ServerId = serverId + ServerId = serverId, + Trust = trust, }; db.PgDbContext.ConnectionLog.Add(connectionLog); diff --git a/Content.Server/Database/ServerDbSqlite.cs b/Content.Server/Database/ServerDbSqlite.cs index 58d21acb63..623b979f4b 100644 --- a/Content.Server/Database/ServerDbSqlite.cs +++ b/Content.Server/Database/ServerDbSqlite.cs @@ -9,6 +9,7 @@ using Content.Server.IP; using Content.Server.Preferences.Managers; using Content.Shared.CCVar; +using Content.Shared.Database; using Microsoft.EntityFrameworkCore; using Robust.Shared.Configuration; using Robust.Shared.Network; @@ -80,22 +81,24 @@ public ServerDbSqlite( public override async Task GetServerBanAsync( IPAddress? address, NetUserId? userId, - ImmutableArray? hwId) + ImmutableArray? hwId, + ImmutableArray>? modernHWIds) { await using var db = await GetDbImpl(); - return (await GetServerBanQueryAsync(db, address, userId, hwId, includeUnbanned: false)).FirstOrDefault(); + return (await GetServerBanQueryAsync(db, address, userId, hwId, modernHWIds, includeUnbanned: false)).FirstOrDefault(); } public override async Task> GetServerBansAsync( IPAddress? address, NetUserId? userId, ImmutableArray? hwId, + ImmutableArray>? modernHWIds, bool includeUnbanned) { await using var db = await GetDbImpl(); - return (await GetServerBanQueryAsync(db, address, userId, hwId, includeUnbanned)).ToList(); + return (await GetServerBanQueryAsync(db, address, userId, hwId, modernHWIds, includeUnbanned)).ToList(); } private async Task> GetServerBanQueryAsync( @@ -103,6 +106,7 @@ private async Task> GetServerBanQueryAsync( IPAddress? address, NetUserId? userId, ImmutableArray? hwId, + ImmutableArray>? modernHWIds, bool includeUnbanned) { var exempt = await GetBanExemptionCore(db, userId); @@ -119,6 +123,7 @@ private async Task> GetServerBanQueryAsync( UserId = userId, ExemptFlags = exempt ?? default, HWId = hwId, + ModernHWIds = modernHWIds, IsNewPlayer = newPlayer, }; @@ -157,7 +162,7 @@ public override async Task AddServerBanAsync(ServerBanDef serverBan) Reason = serverBan.Reason, Severity = serverBan.Severity, BanningAdmin = serverBan.BanningAdmin?.UserId, - HWId = serverBan.HWId?.ToArray(), + HWId = serverBan.HWId, BanTime = serverBan.BanTime.UtcDateTime, ExpirationTime = serverBan.ExpirationTime?.UtcDateTime, RoundId = serverBan.RoundId, @@ -201,6 +206,7 @@ public override async Task> GetServerRoleBansAsync( IPAddress? address, NetUserId? userId, ImmutableArray? hwId, + ImmutableArray>? modernHWIds, bool includeUnbanned) { await using var db = await GetDbImpl(); @@ -210,7 +216,7 @@ public override async Task> GetServerRoleBansAsync( var queryBans = await GetAllRoleBans(db.SqliteDbContext, includeUnbanned); return queryBans - .Where(b => RoleBanMatches(b, address, userId, hwId)) + .Where(b => RoleBanMatches(b, address, userId, hwId, modernHWIds)) .Select(ConvertRoleBan) .ToList()!; } @@ -233,7 +239,8 @@ private static bool RoleBanMatches( ServerRoleBan ban, IPAddress? address, NetUserId? userId, - ImmutableArray? hwId) + ImmutableArray? hwId, + ImmutableArray>? modernHWIds) { if (address != null && ban.Address is not null && address.IsInSubnet(ban.Address.ToTuple().Value)) { @@ -245,7 +252,27 @@ private static bool RoleBanMatches( return true; } - return hwId is { Length: > 0 } hwIdVar && hwIdVar.AsSpan().SequenceEqual(ban.HWId); + switch (ban.HWId?.Type) + { + case HwidType.Legacy: + if (hwId is { Length: > 0 } hwIdVar && hwIdVar.AsSpan().SequenceEqual(ban.HWId.Hwid)) + return true; + break; + + case HwidType.Modern: + if (modernHWIds != null) + { + foreach (var modernHWId in modernHWIds) + { + if (modernHWId.AsSpan().SequenceEqual(ban.HWId.Hwid)) + return true; + } + } + + break; + } + + return false; } public override async Task AddServerRoleBanAsync(ServerRoleBanDef serverBan) @@ -258,7 +285,7 @@ public override async Task AddServerRoleBanAsync(ServerRoleBan Reason = serverBan.Reason, Severity = serverBan.Severity, BanningAdmin = serverBan.BanningAdmin?.UserId, - HWId = serverBan.HWId?.ToArray(), + HWId = serverBan.HWId, BanTime = serverBan.BanTime.UtcDateTime, ExpirationTime = serverBan.ExpirationTime?.UtcDateTime, RoundId = serverBan.RoundId, @@ -312,7 +339,7 @@ public override async Task AddServerRoleUnbanAsync(ServerRoleUnbanDef serverUnba ban.Id, uid, ban.Address.ToTuple(), - ban.HWId == null ? null : ImmutableArray.Create(ban.HWId), + ban.HWId, // SQLite apparently always reads DateTime as unspecified, but we always write as UTC. DateTime.SpecifyKind(ban.BanTime, DateTimeKind.Utc), ban.ExpirationTime == null ? null : DateTime.SpecifyKind(ban.ExpirationTime.Value, DateTimeKind.Utc), @@ -372,7 +399,7 @@ public override async Task AddServerRoleUnbanAsync(ServerRoleUnbanDef serverUnba ban.Id, uid, ban.Address.ToTuple(), - ban.HWId == null ? null : ImmutableArray.Create(ban.HWId), + ban.HWId, // SQLite apparently always reads DateTime as unspecified, but we always write as UTC. DateTime.SpecifyKind(ban.BanTime, DateTimeKind.Utc), ban.ExpirationTime == null ? null : DateTime.SpecifyKind(ban.ExpirationTime.Value, DateTimeKind.Utc), @@ -408,7 +435,8 @@ public override async Task AddConnectionLogAsync( NetUserId userId, string userName, IPAddress address, - ImmutableArray hwId, + ImmutableTypedHwid? hwId, + float trust, ConnectionDenyReason? denied, int serverId) { @@ -420,9 +448,10 @@ public override async Task AddConnectionLogAsync( Time = DateTime.UtcNow, UserId = userId.UserId, UserName = userName, - HWId = hwId.ToArray(), + HWId = hwId, Denied = denied, - ServerId = serverId + ServerId = serverId, + Trust = trust, }; db.SqliteDbContext.ConnectionLog.Add(connectionLog); diff --git a/Content.Server/Database/ServerRoleBanDef.cs b/Content.Server/Database/ServerRoleBanDef.cs index f615d5da4d..dda3a82237 100644 --- a/Content.Server/Database/ServerRoleBanDef.cs +++ b/Content.Server/Database/ServerRoleBanDef.cs @@ -1,4 +1,3 @@ -using System.Collections.Immutable; using System.Net; using Content.Shared.Database; using Robust.Shared.Network; @@ -10,7 +9,7 @@ public sealed class ServerRoleBanDef public int? Id { get; } public NetUserId? UserId { get; } public (IPAddress address, int cidrMask)? Address { get; } - public ImmutableArray? HWId { get; } + public ImmutableTypedHwid? HWId { get; } public DateTimeOffset BanTime { get; } public DateTimeOffset? ExpirationTime { get; } @@ -26,7 +25,7 @@ public ServerRoleBanDef( int? id, NetUserId? userId, (IPAddress, int)? address, - ImmutableArray? hwId, + ImmutableTypedHwid? hwId, DateTimeOffset banTime, DateTimeOffset? expirationTime, int? roundId, diff --git a/Content.Server/Fluids/EntitySystems/AbsorbentSystem.Footprints.cs b/Content.Server/Fluids/EntitySystems/AbsorbentSystem.Footprints.cs deleted file mode 100644 index 50b5b7a660..0000000000 --- a/Content.Server/Fluids/EntitySystems/AbsorbentSystem.Footprints.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.Linq; -using Content.Shared.Chemistry.Components; -using Content.Shared.Fluids; -using Content.Shared.FootPrint; - -namespace Content.Server.Fluids.EntitySystems; - -public sealed partial class AbsorbentSystem -{ - [Dependency] private readonly EntityLookupSystem _lookup = default!; - - /// - /// Tries to clean a number of footprints in a range determined by the component. Returns the number of cleaned footprints. - /// - private int TryCleanNearbyFootprints(EntityUid user, EntityUid used, Entity target, Entity absorbentSoln) - { - var footprintQuery = GetEntityQuery(); - var targetCoords = Transform(target).Coordinates; - var entities = _lookup.GetEntitiesInRange(targetCoords, target.Comp.FootprintCleaningRange, LookupFlags.Uncontained); - - // Take up to [MaxCleanedFootprints] footprints closest to the target - var cleaned = entities.AsEnumerable() - .Select(uid => (uid, dst: Transform(uid).Coordinates.TryDistance(EntityManager, _transform, targetCoords, out var dst) ? dst : 0f)) - .Where(ent => ent.dst > 0f) - .OrderBy(ent => ent.dst) - .Select(ent => (ent.uid, comp: footprintQuery.GetComponent(ent.uid))); - - // And try to interact with each one of them, ignoring useDelay - var processed = 0; - foreach (var (uid, footprintComp) in cleaned) - { - if (TryPuddleInteract(user, used, uid, target.Comp, useDelay: null, absorbentSoln)) - processed++; - - if (processed >= target.Comp.MaxCleanedFootprints) - break; - } - - return processed; - } -} diff --git a/Content.Server/Fluids/EntitySystems/AbsorbentSystem.cs b/Content.Server/Fluids/EntitySystems/AbsorbentSystem.cs index 1f8c44a240..52afdcf8b4 100644 --- a/Content.Server/Fluids/EntitySystems/AbsorbentSystem.cs +++ b/Content.Server/Fluids/EntitySystems/AbsorbentSystem.cs @@ -1,4 +1,5 @@ using System.Numerics; +using Content.Server.Chemistry.Containers.EntitySystems; using Content.Server.Popups; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.EntitySystems; @@ -17,7 +18,7 @@ namespace Content.Server.Fluids.EntitySystems; /// -public sealed partial class AbsorbentSystem : SharedAbsorbentSystem +public sealed class AbsorbentSystem : SharedAbsorbentSystem { [Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly AudioSystem _audio = default!; @@ -25,7 +26,7 @@ public sealed partial class AbsorbentSystem : SharedAbsorbentSystem [Dependency] private readonly PuddleSystem _puddleSystem = default!; [Dependency] private readonly SharedMeleeWeaponSystem _melee = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; - [Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!; + [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; [Dependency] private readonly UseDelaySystem _useDelay = default!; [Dependency] private readonly MapSystem _mapSystem = default!; @@ -118,8 +119,6 @@ public void Mop(EntityUid user, EntityUid target, EntityUid used, AbsorbentCompo if (!TryRefillableInteract(user, used, target, component, useDelay, absorberSoln.Value)) return; } - - TryCleanNearbyFootprints(user, used, (target, component), absorberSoln.Value); } /// diff --git a/Content.Server/FootPrint/FootPrintsSystem.cs b/Content.Server/FootPrint/FootPrintsSystem.cs index 524fcd1cec..0e45acff5c 100644 --- a/Content.Server/FootPrint/FootPrintsSystem.cs +++ b/Content.Server/FootPrint/FootPrintsSystem.cs @@ -1,39 +1,39 @@ -using System.Linq; using Content.Server.Atmos.Components; using Content.Shared.Inventory; +using Content.Shared.Mobs; +using Content.Shared.Mobs.Components; using Content.Shared.FootPrint; using Content.Shared.Standing; using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.EntitySystems; -using Content.Shared.Forensics; using Robust.Shared.Map; -using Robust.Shared.Physics.Components; -using Robust.Shared.Prototypes; using Robust.Shared.Random; namespace Content.Server.FootPrint; - public sealed class FootPrintsSystem : EntitySystem { [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly IPrototypeManager _protoMan = default!; - [Dependency] private readonly InventorySystem _inventory = default!; + [Dependency] private readonly IMapManager _map = default!; + [Dependency] private readonly SharedSolutionContainerSystem _solution = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; - [Dependency] private readonly EntityLookupSystem _lookup = default!; + private EntityQuery _transformQuery; + private EntityQuery _mobThresholdQuery; private EntityQuery _appearanceQuery; - private EntityQuery _standingStateQuery; + private EntityQuery _layingQuery; public override void Initialize() { base.Initialize(); + _transformQuery = GetEntityQuery(); + _mobThresholdQuery = GetEntityQuery(); _appearanceQuery = GetEntityQuery(); - _standingStateQuery = GetEntityQuery(); + _layingQuery = GetEntityQuery(); SubscribeLocalEvent(OnStartupComponent); SubscribeLocalEvent(OnMove); @@ -46,72 +46,62 @@ private void OnStartupComponent(EntityUid uid, FootPrintsComponent component, Co private void OnMove(EntityUid uid, FootPrintsComponent component, ref MoveEvent args) { - if (component.ContainedSolution.Volume <= 0 - || TryComp(uid, out var physics) && physics.BodyStatus != BodyStatus.OnGround - || args.Entity.Comp1.GridUid is not {} gridUid) + if (component.PrintsColor.A <= 0f + || !_transformQuery.TryComp(uid, out var transform) + || !_mobThresholdQuery.TryComp(uid, out var mobThreshHolds) + || !_map.TryFindGridAt(_transform.GetMapCoordinates((uid, transform)), out var gridUid, out _)) return; - var newPos = _transform.ToMapCoordinates(args.NewPosition).Position; - var dragging = _standingStateQuery.TryComp(uid, out var standing) && standing.CurrentState == StandingState.Lying; - var distance = (newPos - component.LastStepPos).Length(); + var dragging = mobThreshHolds.CurrentThresholdState is MobState.Critical or MobState.Dead + || _layingQuery.TryComp(uid, out var laying) && laying.IsCrawlingUnder; + var distance = (transform.LocalPosition - component.StepPos).Length(); var stepSize = dragging ? component.DragSize : component.StepSize; - if (distance < stepSize) + if (!(distance > stepSize)) return; - // are we on a puddle? we exit, ideally we would exchange liquid and DNA with the puddle but meh, too lazy to do that now. - var entities = _lookup.GetEntitiesIntersecting(uid, LookupFlags.All); - if (entities.Any(HasComp)) - return; - - // Spawn the footprint - var footprintUid = Spawn(component.StepProtoId, CalcCoords(gridUid, component, args.Component, dragging)); - var stepTransform = Transform(footprintUid); - var footPrintComponent = EnsureComp(footprintUid); + component.RightStep = !component.RightStep; - // transfer owner DNA into the footsteps - var forensics = EntityManager.EnsureComponent(footprintUid); - if (TryComp(uid, out var ownerForensics)) - forensics.DNAs.UnionWith(ownerForensics.DNAs); + var entity = Spawn(component.StepProtoId, CalcCoords(gridUid, component, transform, dragging)); + var footPrintComponent = EnsureComp(entity); footPrintComponent.PrintOwner = uid; - Dirty(footprintUid, footPrintComponent); + Dirty(entity, footPrintComponent); - if (_appearanceQuery.TryComp(footprintUid, out var appearance)) + if (_appearanceQuery.TryComp(entity, out var appearance)) { - var color = component.ContainedSolution.GetColor(_protoMan); - color.A = Math.Max(0.3f, component.ContainedSolution.FillFraction); - - _appearance.SetData(footprintUid, FootPrintVisualState.State, PickState(uid, dragging), appearance); - _appearance.SetData(footprintUid, FootPrintVisualState.Color, color, appearance); + _appearance.SetData(entity, FootPrintVisualState.State, PickState(uid, dragging), appearance); + _appearance.SetData(entity, FootPrintVisualState.Color, component.PrintsColor, appearance); } + if (!_transformQuery.TryComp(entity, out var stepTransform)) + return; + stepTransform.LocalRotation = dragging - ? (newPos - component.LastStepPos).ToAngle() + Angle.FromDegrees(-90f) - : args.Component.LocalRotation + Angle.FromDegrees(180f); + ? (transform.LocalPosition - component.StepPos).ToAngle() + Angle.FromDegrees(-90f) + : transform.LocalRotation + Angle.FromDegrees(180f); - if (!TryComp(footprintUid, out var solutionContainer) - || !_solution.ResolveSolution((footprintUid, solutionContainer), footPrintComponent.SolutionName, ref footPrintComponent.Solution, out var solution)) - return; + component.PrintsColor = component.PrintsColor.WithAlpha(Math.Max(0f, component.PrintsColor.A - component.ColorReduceAlpha)); + component.StepPos = transform.LocalPosition; - // Transfer from the component to the footprint - var removedReagents = component.ContainedSolution.SplitSolution(component.FootprintVolume); - _solution.ForceAddSolution(footPrintComponent.Solution.Value, removedReagents); + if (!TryComp(entity, out var solutionContainer) + || !_solution.ResolveSolution((entity, solutionContainer), footPrintComponent.SolutionName, ref footPrintComponent.Solution, out var solution) + || string.IsNullOrWhiteSpace(component.ReagentToTransfer) || solution.Volume >= 1) + return; - component.RightStep = !component.RightStep; - component.LastStepPos = newPos; + _solution.TryAddReagent(footPrintComponent.Solution.Value, component.ReagentToTransfer, 1, out _); } private EntityCoordinates CalcCoords(EntityUid uid, FootPrintsComponent component, TransformComponent transform, bool state) { if (state) - return new(uid, transform.LocalPosition); + return new EntityCoordinates(uid, transform.LocalPosition); var offset = component.RightStep ? new Angle(Angle.FromDegrees(180f) + transform.LocalRotation).RotateVec(component.OffsetPrint) : new Angle(transform.LocalRotation).RotateVec(component.OffsetPrint); - return new(uid, transform.LocalPosition + offset); + return new EntityCoordinates(uid, transform.LocalPosition + offset); } private FootPrintVisuals PickState(EntityUid uid, bool dragging) diff --git a/Content.Server/FootPrint/PuddleFootPrintsSystem.cs b/Content.Server/FootPrint/PuddleFootPrintsSystem.cs index a778bcf2c7..706ba25359 100644 --- a/Content.Server/FootPrint/PuddleFootPrintsSystem.cs +++ b/Content.Server/FootPrint/PuddleFootPrintsSystem.cs @@ -1,17 +1,16 @@ +using System.Linq; using Content.Shared.FootPrint; +using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.EntitySystems; -using Content.Shared.FixedPoint; +using Content.Shared.Fluids; using Content.Shared.Fluids.Components; -using Content.Shared.Forensics; using Robust.Shared.Physics.Events; -using Robust.Shared.Prototypes; - namespace Content.Server.FootPrint; public sealed class PuddleFootPrintsSystem : EntitySystem { - [Dependency] private readonly IPrototypeManager _protoMan = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!; public override void Initialize() @@ -22,27 +21,32 @@ public override void Initialize() private void OnStepTrigger(EntityUid uid, PuddleFootPrintsComponent component, ref EndCollideEvent args) { - if (!TryComp(uid, out var puddle) || !TryComp(args.OtherEntity, out var tripper)) + if (!TryComp(uid, out var appearance) + || !TryComp(uid, out var puddle) + || !TryComp(args.OtherEntity, out var tripper) + || !TryComp(uid, out var solutionManager) + || !_solutionContainer.ResolveSolution((uid, solutionManager), puddle.SolutionName, ref puddle.Solution, out var solutions)) return; - // Transfer DNAs from the puddle to the tripper - if (TryComp(uid, out var puddleForensics)) - { - tripper.DNAs.UnionWith(puddleForensics.DNAs); - if(TryComp(args.OtherEntity, out var tripperForensics)) - tripperForensics.DNAs.UnionWith(puddleForensics.DNAs); - } - - // Transfer reagents from the puddle to the tripper. - // Ideally it should be a two-way process, but that is too hard to simulate and will have very little effect outside of potassium-water spills. - var quantity = puddle.Solution?.Comp?.Solution?.Volume ?? 0; - var footprintsCapacity = tripper.ContainedSolution.AvailableVolume; + var totalSolutionQuantity = solutions.Contents.Sum(sol => (float) sol.Quantity); + var waterQuantity = (from sol in solutions.Contents where sol.Reagent.Prototype == "Water" select (float) sol.Quantity).FirstOrDefault(); - if (quantity <= 0 || footprintsCapacity <= 0) + if (waterQuantity / (totalSolutionQuantity / 100f) > component.OffPercent || solutions.Contents.Count <= 0) return; - var transferAmount = FixedPoint2.Min(footprintsCapacity, quantity * component.SizeRatio); - var transferred = _solutionContainer.SplitSolution(puddle.Solution!.Value, transferAmount); - tripper.ContainedSolution.AddSolution(transferred, _protoMan); + tripper.ReagentToTransfer = + solutions.Contents.Aggregate((l, r) => l.Quantity > r.Quantity ? l : r).Reagent.Prototype; + + if (_appearance.TryGetData(uid, PuddleVisuals.SolutionColor, out var color, appearance) + && _appearance.TryGetData(uid, PuddleVisuals.CurrentVolume, out var volume, appearance)) + AddColor((Color) color, (float) volume * component.SizeRatio, tripper); + + _solutionContainer.RemoveEachReagent(puddle.Solution.Value, 1); + } + + private void AddColor(Color col, float quantity, FootPrintsComponent component) + { + component.PrintsColor = component.ColorQuantity == 0f ? col : Color.InterpolateBetween(component.PrintsColor, col, component.ColorInterpolationFactor); + component.ColorQuantity += quantity; } } diff --git a/Content.Server/Projectiles/ProjectileSystem.cs b/Content.Server/Projectiles/ProjectileSystem.cs index 28dbc32e05..436221103d 100644 --- a/Content.Server/Projectiles/ProjectileSystem.cs +++ b/Content.Server/Projectiles/ProjectileSystem.cs @@ -1,15 +1,12 @@ using Content.Server.Administration.Logs; -using Content.Server.Damage.Systems; using Content.Server.Effects; using Content.Server.Weapons.Ranged.Systems; using Content.Shared.Camera; using Content.Shared.Damage; -using Content.Shared.Damage.Events; using Content.Shared.Database; using Content.Shared.Projectiles; using Robust.Shared.Physics.Events; using Robust.Shared.Player; -using Robust.Shared.Utility; namespace Content.Server.Projectiles; @@ -25,7 +22,6 @@ public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnStartCollide); - SubscribeLocalEvent(OnDamageExamine, after: [typeof(DamageOtherOnHitSystem)]); } private void OnStartCollide(EntityUid uid, ProjectileComponent component, ref StartCollideEvent args) @@ -79,21 +75,4 @@ private void OnStartCollide(EntityUid uid, ProjectileComponent component, ref St if (component.ImpactEffect != null && TryComp(uid, out TransformComponent? xform)) RaiseNetworkEvent(new ImpactEffectEvent(component.ImpactEffect, GetNetCoordinates(xform.Coordinates)), Filter.Pvs(xform.Coordinates, entityMan: EntityManager)); } - - private void OnDamageExamine(EntityUid uid, EmbeddableProjectileComponent component, ref DamageExamineEvent args) - { - if (!component.EmbedOnThrow) - return; - - if (!args.Message.IsEmpty) - args.Message.PushNewline(); - - var isHarmful = TryComp(uid, out var passiveDamage) && passiveDamage.Damage.AnyPositive(); - var loc = isHarmful - ? "damage-examine-embeddable-harmful" - : "damage-examine-embeddable"; - - var staminaCostMarkup = FormattedMessage.FromMarkupOrThrow(Loc.GetString(loc)); - args.Message.AddMessage(staminaCostMarkup); - } } diff --git a/Content.Server/StationEvents/Events/PirateRadioSpawnRule.cs b/Content.Server/StationEvents/Events/PirateRadioSpawnRule.cs index 7e5a0ac24e..518d6409bf 100644 --- a/Content.Server/StationEvents/Events/PirateRadioSpawnRule.cs +++ b/Content.Server/StationEvents/Events/PirateRadioSpawnRule.cs @@ -1,18 +1,18 @@ using Robust.Server.GameObjects; using Robust.Server.Maps; using Robust.Shared.Configuration; -using Robust.Shared.Map; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Content.Server.GameTicking; using Content.Server.StationEvents.Components; -using Content.Server.Station.Components; -using Content.Server.Station.Systems; -using Content.Shared.CCVar; -using Content.Shared.GameTicking.Components; -using Content.Shared.Random.Helpers; using Content.Shared.Salvage; +using Content.Shared.Random.Helpers; using System.Linq; +using Content.Shared.GameTicking.Components; +using Content.Shared.CCVar; +using Robust.Shared.Serialization.Manager; +using Content.Shared.Parallax.Biomes; +using Robust.Shared.Map; namespace Content.Server.StationEvents.Events; @@ -24,28 +24,30 @@ public sealed class PirateRadioSpawnRule : StationEventSystem(); - foreach (var stations in station) - { - if (TryComp(stations, out var data) && _station.GetLargestGrid(data) is { } grid) - stationGrids.Add(grid); - } + + // Remove any station from the list that is on a Planet's surface. + // We have to do this because if we spawn the listening post 1.5 kilometers from the station on a planet + // The server will then attempt to generate 7 billion entities, immediately exceeding the 32bit signed integer limit for EntityUids. + var stationsCopy = _serializationManager.CreateCopy(stations, notNullableOverride: true); + foreach (var station in stationsCopy) + if (HasComp(Transform(station).MapUid)) + stations.Remove(station); // _random forces Test Fails if given an empty list. which is guaranteed to happen during Tests. - if (stationGrids.Count <= 0) + if (stations.Count <= 0) return; - var targetStation = _random.Pick(stationGrids); + var targetStation = _random.Pick(stations); var targetMapId = Transform(targetStation).MapID; if (!_mapSystem.MapExists(targetMapId)) diff --git a/Content.Server/Traits/TraitSystem.Functions.cs b/Content.Server/Traits/TraitSystem.Functions.cs index 29ab1512ae..8e53ff69a5 100644 --- a/Content.Server/Traits/TraitSystem.Functions.cs +++ b/Content.Server/Traits/TraitSystem.Functions.cs @@ -1,4 +1,3 @@ -using Content.Shared.FixedPoint; using Content.Shared.Traits; using JetBrains.Annotations; using Robust.Shared.Prototypes; @@ -12,7 +11,6 @@ using Content.Shared.Mood; using Content.Shared.Traits.Assorted.Components; using Content.Shared.Damage; -using Content.Shared.Damage.Components; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Mobs.Components; @@ -549,39 +547,3 @@ public override void OnPlayerSpawn(EntityUid uid, staminaComponent.Cooldown += CooldownModifier; } } - -/// -/// Used for traits that modify SlowOnDamageComponent. -/// -[UsedImplicitly] -public sealed partial class TraitModifySlowOnDamage : TraitFunction -{ - // - // A flat modifier to add to all damage threshold keys. - // - [DataField, AlwaysPushInheritance] - public float DamageThresholdsModifier; - - // - // A multiplier applied to all speed modifier values. - // The higher the multiplier, the stronger the slowdown. - // - [DataField, AlwaysPushInheritance] - public float SpeedModifierMultiplier = 1f; - - public override void OnPlayerSpawn(EntityUid uid, - IComponentFactory factory, - IEntityManager entityManager, - ISerializationManager serializationManager) - { - if (!entityManager.TryGetComponent(uid, out var slowOnDamage)) - return; - - var newSpeedModifierThresholds = new Dictionary(); - - foreach (var (damageThreshold, speedModifier) in slowOnDamage.SpeedModifierThresholds) - newSpeedModifierThresholds[damageThreshold + DamageThresholdsModifier] = 1 - (1 - speedModifier) * SpeedModifierMultiplier; - - slowOnDamage.SpeedModifierThresholds = newSpeedModifierThresholds; - } -} diff --git a/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs b/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs index 3ed4794508..26f0c20608 100644 --- a/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs +++ b/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs @@ -1,7 +1,6 @@ using Content.Server.Chat.Systems; using Content.Server.CombatMode.Disarm; using Content.Server.Movement.Systems; -using Content.Server.Weapons.Ranged.Systems; using Content.Shared.Actions.Events; using Content.Shared.Administration.Components; using Content.Shared.CombatMode; @@ -45,7 +44,7 @@ public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnSpeechHit); - SubscribeLocalEvent(OnMeleeExamineDamage, after: [typeof(GunSystem)]); + SubscribeLocalEvent(OnMeleeExamineDamage); } private void OnMeleeExamineDamage(EntityUid uid, MeleeWeaponComponent component, ref DamageExamineEvent args) diff --git a/Content.Shared.Database/TypedHwid.cs b/Content.Shared.Database/TypedHwid.cs new file mode 100644 index 0000000000..253375e9db --- /dev/null +++ b/Content.Shared.Database/TypedHwid.cs @@ -0,0 +1,64 @@ +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; + +namespace Content.Shared.Database; + +/// +/// Represents a raw HWID value together with its type. +/// +[Serializable] +public sealed class ImmutableTypedHwid(ImmutableArray hwid, HwidType type) +{ + public readonly ImmutableArray Hwid = hwid; + public readonly HwidType Type = type; + + public override string ToString() + { + var b64 = Convert.ToBase64String(Hwid.AsSpan()); + return Type == HwidType.Modern ? $"V2-{b64}" : b64; + } + + public static bool TryParse(string value, [NotNullWhen(true)] out ImmutableTypedHwid? hwid) + { + var type = HwidType.Legacy; + if (value.StartsWith("V2-", StringComparison.Ordinal)) + { + value = value["V2-".Length..]; + type = HwidType.Modern; + } + + var array = new byte[GetBase64ByteLength(value)]; + if (!Convert.TryFromBase64String(value, array, out _)) + { + hwid = null; + return false; + } + + // ReSharper disable once UseCollectionExpression + // Do not use collection expression, C# compiler is weird and it fails sandbox. + hwid = new ImmutableTypedHwid(ImmutableArray.Create(array), type); + return true; + } + + private static int GetBase64ByteLength(string value) + { + // Why is .NET like this man wtf. + return 3 * (value.Length / 4) - value.TakeLast(2).Count(c => c == '='); + } +} + +/// +/// Represents different types of HWIDs as exposed by the engine. +/// +public enum HwidType +{ + /// + /// The legacy HWID system. Should only be used for checking old existing database bans. + /// + Legacy = 0, + + /// + /// The modern HWID system. + /// + Modern = 1, +} diff --git a/Content.Shared/Administration/BanPanelEuiState.cs b/Content.Shared/Administration/BanPanelEuiState.cs index dd10068e5d..74c340566b 100644 --- a/Content.Shared/Administration/BanPanelEuiState.cs +++ b/Content.Shared/Administration/BanPanelEuiState.cs @@ -25,7 +25,7 @@ public sealed class CreateBanRequest : EuiMessageBase { public string? Player { get; set; } public string? IpAddress { get; set; } - public byte[]? Hwid { get; set; } + public ImmutableTypedHwid? Hwid { get; set; } public uint Minutes { get; set; } public string Reason { get; set; } public NoteSeverity Severity { get; set; } @@ -34,7 +34,7 @@ public sealed class CreateBanRequest : EuiMessageBase public bool UseLastHwid { get; set; } public bool Erase { get; set; } - public CreateBanRequest(string? player, (IPAddress, int)? ipAddress, bool useLastIp, byte[]? hwid, bool useLastHwid, uint minutes, string reason, NoteSeverity severity, string[]? roles, bool erase) + public CreateBanRequest(string? player, (IPAddress, int)? ipAddress, bool useLastIp, ImmutableTypedHwid? hwid, bool useLastHwid, uint minutes, string reason, NoteSeverity severity, string[]? roles, bool erase) { Player = player; IpAddress = ipAddress == null ? null : $"{ipAddress.Value.Item1}/{ipAddress.Value.Item2}"; diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index c93eaa77f9..5a4d2840f3 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -422,7 +422,7 @@ public static readonly CVarDef /// How many traits a character can have at most. /// public static readonly CVarDef GameTraitsMax = - CVarDef.Create("game.traits_max", 14, CVar.REPLICATED); + CVarDef.Create("game.traits_max", 10, CVar.REPLICATED); /// /// How many points a character should start with. diff --git a/Content.Shared/Damage/Systems/SharedDamageOtherOnHitSystem.cs b/Content.Shared/Damage/Systems/SharedDamageOtherOnHitSystem.cs index e9e786a817..8b3d29d734 100644 --- a/Content.Shared/Damage/Systems/SharedDamageOtherOnHitSystem.cs +++ b/Content.Shared/Damage/Systems/SharedDamageOtherOnHitSystem.cs @@ -1,6 +1,5 @@ using Content.Shared.Administration.Logs; using Content.Shared.Camera; -using Content.Shared.CombatMode.Pacification; using Content.Shared.Contests; using Content.Shared.Damage; using Content.Shared.Damage.Components; @@ -40,7 +39,6 @@ public override void Initialize() SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnDoHit); SubscribeLocalEvent(OnThrown); - SubscribeLocalEvent(OnAttemptPacifiedThrow); SubscribeLocalEvent(OnItemToggleMapInit); SubscribeLocalEvent(OnItemToggle); @@ -51,19 +49,19 @@ public override void Initialize() /// private void OnMapInit(EntityUid uid, DamageOtherOnHitComponent component, MapInitEvent args) { - if (TryComp(uid, out var melee)) + if (!TryComp(uid, out var melee)) + return; + + if (component.Damage.Empty) + component.Damage = melee.Damage * component.MeleeDamageMultiplier; + if (component.SoundHit == null) + component.SoundHit = melee.SoundHit; + if (component.SoundNoDamage == null) { - if (component.Damage.Empty) - component.Damage = melee.Damage * component.MeleeDamageMultiplier; - if (component.SoundHit == null) - component.SoundHit = melee.SoundHit; - if (component.SoundNoDamage == null) - { - if (melee.SoundNoDamage != null) - component.SoundNoDamage = melee.SoundNoDamage; - else - component.SoundNoDamage = new SoundCollectionSpecifier("WeakHit"); - } + if (melee.SoundNoDamage != null) + component.SoundNoDamage = melee.SoundNoDamage; + else + component.SoundNoDamage = new SoundCollectionSpecifier("WeakHit"); } RaiseLocalEvent(uid, new DamageOtherOnHitStartupEvent((uid, component))); @@ -183,16 +181,6 @@ private void OnThrown(EntityUid uid, DamageOtherOnHitComponent component, Thrown component.HitQuantity = 0; } - /// - /// Prevent Pacified entities from throwing damaging items. - /// - private void OnAttemptPacifiedThrow(EntityUid uid, DamageOtherOnHitComponent comp, ref AttemptPacifiedThrowEvent args) - { - // Allow healing projectiles, forbid any that do damage - if (comp.Damage.AnyPositive()) - args.Cancel("pacified-cannot-throw"); - } - /// /// Gets the total damage a throwing weapon does. /// diff --git a/Content.Shared/Damage/Systems/SlowOnDamageSystem.cs b/Content.Shared/Damage/Systems/SlowOnDamageSystem.cs index 78650ec5cf..3e50ee3557 100644 --- a/Content.Shared/Damage/Systems/SlowOnDamageSystem.cs +++ b/Content.Shared/Damage/Systems/SlowOnDamageSystem.cs @@ -61,13 +61,17 @@ private void OnDamageChanged(EntityUid uid, SlowOnDamageComponent component, Dam private void OnModifySpeed(Entity ent, ref InventoryRelayedEvent args) { + var dif = 1 - args.Args.Speed; + if (dif <= 0) + return; + // reduces the slowness modifier by the given coefficient - args.Args.Speed = 1 - (1 - args.Args.Speed) * ent.Comp.Modifier; + args.Args.Speed += dif * ent.Comp.Modifier; } private void OnExamined(Entity ent, ref ExaminedEvent args) { - var msg = Loc.GetString("slow-on-damage-modifier-examine", ("mod", Math.Round((1 - ent.Comp.Modifier) * 100))); + var msg = Loc.GetString("slow-on-damage-modifier-examine", ("mod", (1 - ent.Comp.Modifier) * 100)); args.PushMarkup(msg); } diff --git a/Content.Shared/Fluids/AbsorbentComponent.cs b/Content.Shared/Fluids/AbsorbentComponent.cs index 6cda88a72d..450ecc0df6 100644 --- a/Content.Shared/Fluids/AbsorbentComponent.cs +++ b/Content.Shared/Fluids/AbsorbentComponent.cs @@ -38,13 +38,4 @@ public sealed partial class AbsorbentComponent : Component { Params = AudioParams.Default.WithVariation(SharedContentAudioSystem.DefaultVariation).WithVolume(-3f), }; - - [DataField] - public float FootprintCleaningRange = 0.2f; - - /// - /// How many footprints within can be cleaned at once. - /// - [DataField] - public int MaxCleanedFootprints = 5; } diff --git a/Content.Shared/Footprint/FootPrintsComponent.cs b/Content.Shared/Footprint/FootPrintsComponent.cs index 99aa2106cf..2b2c4ed66e 100644 --- a/Content.Shared/Footprint/FootPrintsComponent.cs +++ b/Content.Shared/Footprint/FootPrintsComponent.cs @@ -1,6 +1,4 @@ using System.Numerics; -using Content.Shared.Chemistry.Components; -using Content.Shared.FixedPoint; using Robust.Shared.Prototypes; using Robust.Shared.Utility; @@ -9,17 +7,23 @@ namespace Content.Shared.FootPrint; [RegisterComponent] public sealed partial class FootPrintsComponent : Component { - [DataField] + [ViewVariables(VVAccess.ReadOnly), DataField] public ResPath RsiPath = new("/Textures/Effects/footprints.rsi"); - [DataField] - public string - LeftBarePrint = "footprint-left-bare-human", - RightBarePrint = "footprint-right-bare-human", - ShoesPrint = "footprint-shoes", - SuitPrint = "footprint-suit"; + // all of those are set as a layer + [ViewVariables(VVAccess.ReadOnly), DataField] + public string LeftBarePrint = "footprint-left-bare-human"; - [DataField] + [ViewVariables(VVAccess.ReadOnly), DataField] + public string RightBarePrint = "footprint-right-bare-human"; + + [ViewVariables(VVAccess.ReadOnly), DataField] + public string ShoesPrint = "footprint-shoes"; + + [ViewVariables(VVAccess.ReadOnly), DataField] + public string SuitPrint = "footprint-suit"; + + [ViewVariables(VVAccess.ReadOnly), DataField] public string[] DraggingPrint = [ "dragging-1", @@ -28,10 +32,14 @@ public string "dragging-4", "dragging-5", ]; + // yea, those - [DataField] + [ViewVariables(VVAccess.ReadOnly), DataField] public EntProtoId StepProtoId = "Footstep"; + [ViewVariables(VVAccess.ReadOnly), DataField] + public Color PrintsColor = Color.FromHex("#00000000"); + /// /// The size scaling factor for footprint steps. Must be positive. /// @@ -45,8 +53,20 @@ public string public float DragSize = 0.5f; /// - /// Horizontal offset of the created footprints relative to the center. + /// The amount of color to transfer from the source (e.g., puddle) to the footprint. + /// + [DataField] + public float ColorQuantity; + + /// + /// The factor by which the alpha channel is reduced in subsequent footprints. /// + [DataField] + public float ColorReduceAlpha = 0.1f; + + [DataField] + public string? ReagentToTransfer; + [DataField] public Vector2 OffsetPrint = new(0.1f, 0f); @@ -58,20 +78,11 @@ public string /// /// The position of the last footprint in world coordinates. /// - public Vector2 LastStepPos = Vector2.Zero; - - [DataField] - public HashSet DNAs = new(); + public Vector2 StepPos = Vector2.Zero; /// - /// Reagent volume used for footprints. + /// Controls how quickly the footprint color transitions between steps. + /// Value between 0 and 1, where higher values mean faster color changes. /// - [DataField] - public Solution ContainedSolution = new(3) { CanReact = true, MaxVolume = 5f, }; - - /// - /// Amount of reagents used per footprint. - /// - [DataField] - public FixedPoint2 FootprintVolume = 1f; + public float ColorInterpolationFactor = 0.2f; } diff --git a/Content.Shared/Footprint/PuddleFootPrintsComponent.cs b/Content.Shared/Footprint/PuddleFootPrintsComponent.cs index bea2b6b7a1..0e2ddfe383 100644 --- a/Content.Shared/Footprint/PuddleFootPrintsComponent.cs +++ b/Content.Shared/Footprint/PuddleFootPrintsComponent.cs @@ -1,14 +1,11 @@ -using Content.Shared.FixedPoint; - - namespace Content.Shared.FootPrint; [RegisterComponent] public sealed partial class PuddleFootPrintsComponent : Component { - /// - /// Ratio between puddle volume and the amount of reagents that can be transferred from it. - /// [ViewVariables(VVAccess.ReadWrite)] - public FixedPoint2 SizeRatio = 0.15f; + public float SizeRatio = 0.2f; + + [ViewVariables(VVAccess.ReadWrite)] + public float OffPercent = 80f; } diff --git a/Content.Shared/Movement/Components/SpeedModifiedByContactModifierComponent.cs b/Content.Shared/Movement/Components/SpeedModifiedByContactModifierComponent.cs deleted file mode 100644 index 9a32d3453d..0000000000 --- a/Content.Shared/Movement/Components/SpeedModifiedByContactModifierComponent.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Content.Shared.Movement.Systems; -using Robust.Shared.GameStates; - -namespace Content.Shared.Movement.Components; - -// -// This is used to modify how much an entity is affected by speed modifier contacts. -// -[NetworkedComponent, RegisterComponent] -[AutoGenerateComponentState] -[Access(typeof(SpeedModifierContactsSystem))] -public sealed partial class SpeedModifiedByContactModifierComponent : Component -{ - // - // Numbers greater than 1 amplify the walk speed modifier, and lower numbers lessen the effect. - // - [DataField, AutoNetworkedField] - public float WalkModifierEffectiveness = 1.0f; - - // - // Numbers greater than 1 amplify the sprint speed modifier, and lower numbers lessen the effect. - // - [DataField, AutoNetworkedField] - public float SprintModifierEffectiveness = 1.0f; - - // - // The minimum walk speed multiplier. - // - [DataField, AutoNetworkedField] - public float MinWalkMultiplier = 0.1f; - - // - // The minimum sprint speed multiplier. - // - [DataField, AutoNetworkedField] - public float MinSprintMultiplier = 0.1f; -} diff --git a/Content.Shared/Movement/Systems/SpeedModifierContactsSystem.cs b/Content.Shared/Movement/Systems/SpeedModifierContactsSystem.cs index 94becab9d2..6e1b3a29ae 100644 --- a/Content.Shared/Movement/Systems/SpeedModifierContactsSystem.cs +++ b/Content.Shared/Movement/Systems/SpeedModifierContactsSystem.cs @@ -102,16 +102,6 @@ private void MovementSpeedCheck(EntityUid uid, SpeedModifiedByContactComponent c walkSpeed /= entries; sprintSpeed /= entries; - if (TryComp(uid, out var modifier)) - { - walkSpeed = Math.Max(Math.Min(modifier.MinWalkMultiplier, walkSpeed), - // Similar to the formula for movement slow resist in Deadlock - 1 - (1 - walkSpeed) * modifier.WalkModifierEffectiveness); - - sprintSpeed = Math.Max(Math.Min(modifier.MinSprintMultiplier, sprintSpeed), - 1 - (1 - sprintSpeed) * modifier.SprintModifierEffectiveness); - } - args.ModifySpeed(walkSpeed, sprintSpeed); } diff --git a/Content.Shared/Projectiles/EmbedPassiveDamageSystem.cs b/Content.Shared/Projectiles/EmbedPassiveDamageSystem.cs index 589abf305c..55733ac5bb 100644 --- a/Content.Shared/Projectiles/EmbedPassiveDamageSystem.cs +++ b/Content.Shared/Projectiles/EmbedPassiveDamageSystem.cs @@ -1,4 +1,3 @@ -using Content.Shared.CombatMode.Pacification; using Content.Shared.Damage; using Content.Shared.Damage.Components; using Content.Shared.Damage.Events; @@ -25,7 +24,6 @@ public override void Initialize() SubscribeLocalEvent(OnEmbed); SubscribeLocalEvent(OnRemoveEmbed); SubscribeLocalEvent(OnItemToggle); - SubscribeLocalEvent(OnAttemptPacifiedThrow); } /// @@ -94,16 +92,6 @@ private void OnItemToggle(EntityUid uid, EmbedPassiveDamageComponent component, component.Damage = deactivatedDamage; } - /// - /// Prevent Pacified entities from throwing items that deal passive damage when embedded. - /// - private void OnAttemptPacifiedThrow(EntityUid uid, EmbedPassiveDamageComponent comp, ref AttemptPacifiedThrowEvent args) - { - // Allow healing projectiles, forbid any that do damage - if (comp.Damage.AnyPositive()) - args.Cancel("pacified-cannot-throw"); - } - public override void Update(float frameTime) { base.Update(frameTime); diff --git a/Content.Shared/Projectiles/SharedProjectileSystem.cs b/Content.Shared/Projectiles/SharedProjectileSystem.cs index a32575c0ee..f5d2a915dc 100644 --- a/Content.Shared/Projectiles/SharedProjectileSystem.cs +++ b/Content.Shared/Projectiles/SharedProjectileSystem.cs @@ -1,5 +1,6 @@ using System.Numerics; using Content.Shared.Body.Systems; +using Content.Shared.CombatMode.Pacification; using Content.Shared.Damage; using Content.Shared.DoAfter; using Content.Shared.Examine; @@ -46,6 +47,7 @@ public override void Initialize() SubscribeLocalEvent(OnEmbedThrowDoHit); SubscribeLocalEvent(OnEmbedActivate); SubscribeLocalEvent(OnEmbedRemove); + SubscribeLocalEvent(OnAttemptPacifiedThrow); SubscribeLocalEvent(OnExamined); } @@ -209,6 +211,14 @@ public void SetShooter(EntityUid id, ProjectileComponent component, EntityUid sh Dirty(id, component); } + /// + /// Prevent players with the Pacified status effect from throwing embeddable projectiles. + /// + private void OnAttemptPacifiedThrow(Entity ent, ref AttemptPacifiedThrowEvent args) + { + args.Cancel("pacified-cannot-throw-embed"); + } + private void OnExamined(EntityUid uid, EmbeddableProjectileComponent component, ExaminedEvent args) { if (!(component.Target is {} target)) diff --git a/Content.Shared/Slippery/SlippableModifierComponent.cs b/Content.Shared/Slippery/SlippableModifierComponent.cs deleted file mode 100644 index 9a5a22e3a2..0000000000 --- a/Content.Shared/Slippery/SlippableModifierComponent.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Robust.Shared.GameStates; -namespace Content.Shared.Slippery; - -/// -/// Modifies the duration of slip paralysis on an entity. -/// -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] -public sealed partial class SlippableModifierComponent : Component -{ - /// - /// What to multiply the paralyze time by. - /// - [DataField, AutoNetworkedField] - public float ParalyzeTimeMultiplier = 1f; -} diff --git a/Content.Shared/Slippery/SlipperySystem.cs b/Content.Shared/Slippery/SlipperySystem.cs index 4d6c4fe342..0b52cdde92 100644 --- a/Content.Shared/Slippery/SlipperySystem.cs +++ b/Content.Shared/Slippery/SlipperySystem.cs @@ -37,7 +37,6 @@ public override void Initialize() SubscribeLocalEvent(OnThrownSlipAttempt); // as long as slip-resistant mice are never added, this should be fine (otherwise a mouse-hat will transfer it's power to the wearer). SubscribeLocalEvent>((e, c, ev) => OnNoSlipAttempt(e, c, ev.Args)); - SubscribeLocalEvent(OnSlippableModifierSlipped); } private void HandleStepTrigger(EntityUid uid, SlipperyComponent component, ref StepTriggeredOffEvent args) @@ -63,11 +62,6 @@ private void OnThrownSlipAttempt(EntityUid uid, ThrownItemComponent comp, ref Sl args.Cancelled = true; } - private void OnSlippableModifierSlipped(EntityUid uid, SlippableModifierComponent comp, ref SlippedEvent args) - { - args.ParalyzeTime *= comp.ParalyzeTimeMultiplier; - } - private bool CanSlip(EntityUid uid, EntityUid toSlip) { return !_container.IsEntityInContainer(uid) @@ -92,9 +86,6 @@ private void TrySlip(EntityUid uid, SlipperyComponent component, EntityUid other var ev = new SlipEvent(other); RaiseLocalEvent(uid, ref ev); - var slippedEv = new SlippedEvent(uid, component.ParalyzeTime); - RaiseLocalEvent(other, ref slippedEv); - if (TryComp(other, out PhysicsComponent? physics) && !HasComp(other)) { _physics.SetLinearVelocity(other, physics.LinearVelocity * component.LaunchForwardsMultiplier, body: physics); @@ -109,7 +100,7 @@ private void TrySlip(EntityUid uid, SlipperyComponent component, EntityUid other var playSound = !_statusEffects.HasStatusEffect(other, "KnockedDown"); - _stun.TryParalyze(other, TimeSpan.FromSeconds(slippedEv.ParalyzeTime), true); + _stun.TryParalyze(other, TimeSpan.FromSeconds(component.ParalyzeTime), true); RaiseLocalEvent(other, new MoodEffectEvent("MobSlipped")); @@ -143,19 +134,3 @@ public record struct SlipCausingAttemptEvent (bool Cancelled); /// The entity being slipped [ByRefEvent] public readonly record struct SlipEvent(EntityUid Slipped); - -/// Raised on an entity that slipped. -/// The entity that caused the slip -/// How many seconds the entity will be paralyzed for, modifiable with this event. -[ByRefEvent] -public record struct SlippedEvent -{ - public readonly EntityUid Slipper; - public float ParalyzeTime; - - public SlippedEvent(EntityUid slipper, float paralyzeTime) - { - Slipper = slipper; - ParalyzeTime = paralyzeTime; - } -} diff --git a/Content.Shared/StepTrigger/Systems/StepTriggerSystem.cs b/Content.Shared/StepTrigger/Systems/StepTriggerSystem.cs index 6635e331b5..5bb303cf51 100644 --- a/Content.Shared/StepTrigger/Systems/StepTriggerSystem.cs +++ b/Content.Shared/StepTrigger/Systems/StepTriggerSystem.cs @@ -1,6 +1,5 @@ using Content.Shared.Gravity; using Content.Shared.StepTrigger.Components; -using Content.Shared.Traits.Assorted.Components; using Content.Shared.Whitelist; using Robust.Shared.Map.Components; using Robust.Shared.Physics; @@ -98,11 +97,7 @@ private void UpdateColliding(EntityUid uid, StepTriggerComponent component, Tran // this is hard to explain var intersect = Box2.Area(otherAabb.Intersect(ourAabb)); var ratio = Math.Max(intersect / Box2.Area(otherAabb), intersect / Box2.Area(ourAabb)); - var requiredTriggeredSpeed = component.RequiredTriggeredSpeed; - if (TryComp(otherUid, out var speedModifier)) - requiredTriggeredSpeed *= speedModifier.RequiredTriggeredSpeedModifier; - - if (otherPhysics.LinearVelocity.Length() < requiredTriggeredSpeed + if (otherPhysics.LinearVelocity.Length() < component.RequiredTriggeredSpeed || component.CurrentlySteppedOn.Contains(otherUid) || ratio < component.IntersectRatio || !CanTrigger(uid, otherUid, component)) diff --git a/Content.Shared/Traits/Assorted/Components/TraitSpeedModifierComponent.cs b/Content.Shared/Traits/Assorted/Components/TraitSpeedModifierComponent.cs index c0d79c8375..85dc52a21f 100644 --- a/Content.Shared/Traits/Assorted/Components/TraitSpeedModifierComponent.cs +++ b/Content.Shared/Traits/Assorted/Components/TraitSpeedModifierComponent.cs @@ -13,10 +13,4 @@ public sealed partial class TraitSpeedModifierComponent : Component [DataField, AutoNetworkedField] public float SprintModifier = 1.0f; - - // - // Multiplied with the required trigger speed for step triggers that this entity collides with. - // - [DataField, AutoNetworkedField] - public float RequiredTriggeredSpeedModifier = 1.0f; } diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index da6e4e2058..707e36d75f 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -4051,7 +4051,7 @@ Entries: - type: Add message: >- Glass/Crystal shards and floor tiles now deal damage and embed in - victims when colliding with them. + victims when colliding with them. id: 6107 time: '2024-05-11T01:23:00.0000000+00:00' - author: FoxxoTrystan @@ -4262,7 +4262,7 @@ Entries: - type: Fix message: >- Spent bullet casings now fly away from a shooter in a cinematic manner, - rather than fall at their feet. + rather than fall at their feet. id: 6133 time: '2024-07-01T22:37:29.0000000+00:00' - author: WarMechanic @@ -4458,7 +4458,7 @@ Entries: - type: Add message: >- Cablecuffs and Zipties are now easier to escape out of if you're - smaller. + smaller. id: 6158 time: '2024-07-13T08:05:52.0000000+00:00' - author: FoxxoTrystan @@ -4526,7 +4526,7 @@ Entries: - type: Fix message: >- Harpies can no longer buy Loadout items that are impossible for them to - wear due to having digitigrade legs. + wear due to having digitigrade legs. id: 6168 time: '2024-07-18T02:34:52.0000000+00:00' - author: Mnemotechnician @@ -4585,7 +4585,7 @@ Entries: - type: Add message: >- Camera Shake is now affected by character mass. Small characters - experience more camera shake. Large characters experience less. + experience more camera shake. Large characters experience less. id: 6176 time: '2024-07-21T06:50:25.0000000+00:00' - author: VMSolidus @@ -4593,7 +4593,7 @@ Entries: - type: Add message: >- The Head Of Personnel can now hire people onto basic roles for each - department by vending PDAs. + department by vending PDAs. id: 6177 time: '2024-07-22T19:12:01.0000000+00:00' - author: VMSolidus @@ -4700,7 +4700,7 @@ Entries: - type: Fix message: >- Fixed Tools, Drink Containers, Lockers, and all base items having - inconsistent sound settings. + inconsistent sound settings. id: 6191 time: '2024-07-31T22:47:01.0000000+00:00' - author: VMSolidus @@ -4808,7 +4808,7 @@ Entries: - type: Add message: >- Added a 1-point combat knife to Loadouts for Felinid/Harpy security - jobs. + jobs. - type: Tweak message: Made the security belt and security webbing able to hold combat knives. - type: Tweak @@ -4858,7 +4858,7 @@ Entries: Due to budget cuts, Nanotrasen has ceased stocking Clothesmate vendors with more clothing than the average cargo tech can afford. Civilians are advised to bring their own clothes to the station if they wish to wear - anything other than grey. + anything other than grey. id: 6208 time: '2024-08-04T00:23:53.0000000+00:00' - author: VMSolidus @@ -4866,7 +4866,7 @@ Entries: - type: Add message: >- SweatMAX, "hot foods", Mars Mart, and Nippon-tan vendors have all been - added to vendor spawners. + added to vendor spawners. id: 6209 time: '2024-08-04T06:26:34.0000000+00:00' - author: Skubman @@ -4925,7 +4925,7 @@ Entries: The Carrying system has been reworked as a means of better supporting having extremely large species and characters. 10kg Harpies should no longer be oppressed by 2000kg Lamia with infinitely short carry - attempts. + attempts. id: 6216 time: '2024-08-05T17:11:37.0000000+00:00' - author: Rane @@ -4953,11 +4953,11 @@ Entries: - type: Add message: >- CPR has been added to the game. People with CPR training can now perform - CPR on anyone who is in either crit or dead states. + CPR on anyone who is in either crit or dead states. - type: Add message: >- CPR Training has been added to the game as a new positive trait. All - medical staff start with this trait for free. + medical staff start with this trait for free. id: 6220 time: '2024-08-06T05:28:54.0000000+00:00' - author: Skubman @@ -5144,7 +5144,7 @@ Entries: - type: Fix message: >- Sluggish and Snail-Paced will now properly apply their movement - penalties upon joining. + penalties upon joining. id: 6244 time: '2024-08-09T17:28:01.0000000+00:00' - author: ODJ @@ -5160,7 +5160,7 @@ Entries: - type: Tweak message: >- Renamed "Psionic Mantis" to "Mantis", as it was originally going to be - called. + called. id: 6246 time: '2024-08-10T12:03:12.0000000+00:00' - author: Skubman @@ -5448,7 +5448,7 @@ Entries: - type: Tweak message: >- Chaplain, Mantis, & Mystagogue all receive the Latent Psychic trait for - free, automatically. + free, automatically. id: 6273 time: '2024-08-20T18:42:44.0000000+00:00' - author: VMSolidus @@ -5466,7 +5466,7 @@ Entries: Actions no longer need to hardcode in target blacklists, and can now blacklist entities in YML. This is notably useful for Psionic powers, which all share a common feature that they can't target people with - Psionic Insulation (Or have been Mindbroken). + Psionic Insulation (Or have been Mindbroken). id: 6275 time: '2024-08-21T09:08:40.0000000+00:00' - author: VMSolidus @@ -5535,7 +5535,7 @@ Entries: message: >- Gaining a new Psionic Power can now display messages to alert the player, both as a short popup, and optionally a lengthier message sent - to the user's Chat window. + to the user's Chat window. id: 6284 time: '2024-08-22T17:12:26.0000000+00:00' - author: DEATHB4DEFEAT @@ -5652,7 +5652,7 @@ Entries: message: >- Natural Telepath has returned from Psionic-Refactor V1, now using new functionality from the trait system that allows traits to buy psionic - powers directly. + powers directly. - type: Add message: >- Latent Psychics who have neither bought Natural Telepath, nor acquired @@ -5730,7 +5730,7 @@ Entries: message: >- InnatePsionicPowers now operates on MapInitEvent instead of ComponentStartup, meaning that it can now be safely used on entities - that are mapped in instead of spawned. + that are mapped in instead of spawned. - type: Add message: >- Oracle and Sophia are now recognized as Divine, and as such are @@ -5783,7 +5783,7 @@ Entries: Psionic(But cannot gain powers randomly). This is due to their status as "Magical And/Or Extraplanar Creatures", which makes them valid targets for anti-psychic abilities such as the Psionic Mantis' Anti-Psychic - Knife. + Knife. - type: Add message: Some Reagent Slimes can now contain Lotophagoi Oil. id: 6310 @@ -5800,7 +5800,7 @@ Entries: message: >- "Clone Soup" scales directly with the mass of the entity you're attempting to clone. Fail to clone a Lamia, and you'll be greeted with - an Olympic swimming pool worth of blood when the machine opens. + an Olympic swimming pool worth of blood when the machine opens. - type: Add message: >- Cloning will fail if at any point during the procedure, the machine is @@ -5809,7 +5809,7 @@ Entries: message: >- Clones come out of the machine with severe Cellular damage. Consider using Doxarubixadone in a Cryo tube as an affordable means of - "Finishing" clones. + "Finishing" clones. - type: Tweak message: >- Cloning Time is now increased proportionally if an entity being cloned @@ -5867,7 +5867,7 @@ Entries: - type: Add message: >- Several new tips have been added to the game, many of which reference - new content available on Einstein-Engines. + new content available on Einstein-Engines. id: 6315 time: '2024-09-05T00:00:21.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/844 @@ -5885,7 +5885,7 @@ Entries: Due to NUMEROUS complaints, NanoTrasen has swapped the sticker labels on all colored jumpskirts to correctly state that they are infact, "Skirts", so now they can legally be worn by Harpies, Lamia, and - Arachne. + Arachne. id: 6317 time: '2024-09-05T00:19:49.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/848 @@ -5949,7 +5949,7 @@ Entries: message: >- All non-job specific uniforms have had their points reduced drastically, almost all of them down to 0 points, with only a tiny handful at 2 - points, and the rest as 1. + points, and the rest as 1. id: 6321 time: '2024-09-07T00:53:13.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/872 @@ -6126,7 +6126,7 @@ Entries: - type: Add message: >- The Antag Refactor has been once again added back in. More antags will - be coming soon. + be coming soon. id: 6340 time: '2024-09-09T14:21:56.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/794 @@ -6234,7 +6234,7 @@ Entries: - type: Add message: >- added rcds to the list of possible re and ported over the re update from - delta + delta - type: Tweak message: >- tweaked the costs of boards re-machine boards to be more consistent with @@ -6425,7 +6425,7 @@ Entries: - type: Fix message: >- Cloning Consoles will now correctly state when a body has the - Uncloneable trait. + Uncloneable trait. id: 6368 time: '2024-09-18T04:30:39.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/926 @@ -6544,7 +6544,7 @@ Entries: - type: Add message: >- All "Long-arms", Rifles, Light Machine Guns, Shotguns, now require - wielding to use. + wielding to use. id: 6378 time: '2024-09-20T20:35:43.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/913 @@ -6562,7 +6562,7 @@ Entries: - type: Add message: >- Tau-Ceti Basic, Tradeband, Freespeak, Elyran Standard, Sol Common, and - Sign Language have been added to the Languages tab. + Sign Language have been added to the Languages tab. id: 6380 time: '2024-09-20T21:46:47.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/936 @@ -6572,7 +6572,7 @@ Entries: message: >- Removed the "Become Psionic" traitor objective. It was literally impossible to fail if you took the Latent Psychic trait, and literally - impossible to greentext if you didn't. + impossible to greentext if you didn't. id: 6381 time: '2024-09-20T21:47:16.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/940 @@ -6643,7 +6643,7 @@ Entries: message: >- The Do-After bar for Breath of Life and Healing Word is now hidden if glimmer is low enough. The threshold for which scales with your - Dampening stat. More Dampening = stealthier casting. + Dampening stat. More Dampening = stealthier casting. id: 6386 time: '2024-09-24T00:56:34.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/949 @@ -6683,7 +6683,7 @@ Entries: - type: Remove message: >- The button for "Suit/Skirt Preference" has also been removed. Suit - selection is done via Loadouts. + selection is done via Loadouts. id: 6390 time: '2024-09-28T00:46:19.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/957 @@ -6789,7 +6789,7 @@ Entries: message: >- Nerfed the fuck out of Reagent Slimes. They have 1/3rd as much HP, spawn half as many during reagent slime vents, deal significantly less damage, - and inject a lot less chems when biting people. + and inject a lot less chems when biting people. id: 6401 time: '2024-09-29T17:42:43.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/977 @@ -6836,7 +6836,7 @@ Entries: message: >- Oni now have a built-in negative trait that acts as a more severe version of Low Psi-Potential. Oni now have significant penalties to - generating new Psionic Powers. + generating new Psionic Powers. id: 6406 time: '2024-09-30T18:28:31.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/983 @@ -6987,7 +6987,7 @@ Entries: message: >- Nerfed Lotophagoi Moodlets so that both the positive and negative moodlets only last for 10 minutes. The total mood modification has been - left untouched. + left untouched. id: 6423 time: '2024-10-07T15:29:05.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1013 @@ -7005,7 +7005,7 @@ Entries: - type: Add message: >- The Salvage Shittle has been replaced with the Pathfinder Expedition - Vessel from Frontier. + Vessel from Frontier. id: 6425 time: '2024-10-09T01:17:11.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1027 @@ -7208,7 +7208,7 @@ Entries: message: >- Added 8 new Physical Traits. These are, Cyber-Eyes Basic System(Plus 4 different modular options), Bionic Arm, Dermal Armor, and Platelet - Factories. + Factories. id: 6445 time: '2024-10-13T19:25:05.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1035 @@ -7220,7 +7220,7 @@ Entries: station staff. New loadout items for corporate contractors are now available for Bartender, Botanist, Cataloguer, Chef, and Janitor. Currently there are no jumpskirt versions of these uniforms. We are - looking for people willing to make skirt variations of each of these. + looking for people willing to make skirt variations of each of these. id: 6446 time: '2024-10-13T19:26:23.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1041 @@ -7286,7 +7286,7 @@ Entries: - type: Tweak message: >- Psionic Rolls should now generate larger, random amounts of Potentia. - This should make it a lot easier to obtain powers. + This should make it a lot easier to obtain powers. id: 6453 time: '2024-10-15T23:13:13.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1047 @@ -7384,7 +7384,7 @@ Entries: message: >- The Syndicate has significantly expanded their Listening Outpost operations, featuring larger, better defended outposts. These new - outposts also serve as supply depots for Syndicate agents in the field. + outposts also serve as supply depots for Syndicate agents in the field. id: 6465 time: '2024-10-19T08:45:44.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1065 @@ -7591,7 +7591,7 @@ Entries: message: >- The Syndicate Listening Post now has a custom communications console, which no longer can recall Nanotrasen shuttles, and doesn't sign its - messages as Nuclear Operatives. + messages as Nuclear Operatives. id: 6490 time: '2024-10-27T15:31:16.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1143 @@ -7646,12 +7646,12 @@ Entries: New psi-power "Summon Imp", as the first new Psi Familiar. Imps are small motes of living flame that follow and protect their summoner. Imps also emit an incredibly bright light, and can natively cast Pyrokinetic - Flare. + Flare. - type: Add message: >- Remilia has been updated to be a Psi Familiar, and no longer requires the Bible to summon. Chaplains now start with a Power that lets them - summon Remilia once every 2 minutes. + summon Remilia once every 2 minutes. id: 6495 time: '2024-10-27T16:51:18.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1146 @@ -7690,7 +7690,7 @@ Entries: - type: Tweak message: >- Literally doubled the power output of solar panels, to make them - actually worthwhile to setup. + actually worthwhile to setup. id: 6498 time: '2024-11-02T00:40:50.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1170 @@ -7744,7 +7744,7 @@ Entries: - type: Add message: >- lockers for Syndicate Agent (for mapping), Janitor and Bartender have - been added. + been added. - type: Add message: a new wall variant has been added for the radiation closet - type: Add @@ -7781,7 +7781,7 @@ Entries: message: >- Communications Consoles that are marked as "Can't Recall The Shuttle" now can't recall the shuttle. Previously they were still able to recall - it. + it. id: 6506 time: '2024-11-05T04:12:36.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1180 @@ -7829,7 +7829,7 @@ Entries: - type: Remove message: >- Shadowkin have been temporarily disabled pending significant reworks and - balance updates. + balance updates. id: 6512 time: '2024-11-08T19:33:53.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1187 @@ -7849,7 +7849,7 @@ Entries: - type: Fix message: >- Fixed AddTrait and ReplaceTrait functions giving players an unmodified, - completely default component. + completely default component. id: 6514 time: '2024-11-10T15:22:47.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1195 @@ -7991,7 +7991,7 @@ Entries: - type: Add message: >- The stamina cost of an object's power attack can now be revealed by - examining its damage values. + examining its damage values. - type: Tweak message: >- Power attacks can't be done if your stamina is too low, so you can't @@ -8056,12 +8056,12 @@ Entries: - type: Tweak message: >- CyberEyes Basic System has been split, now Flash Protection is a - separate module. + separate module. - type: Add message: >- Dermal Armor no longer replaces your original species damage resistances. It now stacks multiplicatively with your original - resistances. + resistances. - type: Tweak message: Dermal Armor can now be taken by any species, not just Humans. - type: Add @@ -8234,7 +8234,7 @@ Entries: - type: Fix message: >- Fixed biosynthetic and other printable parts not allowing you to attach - body parts to them. + body parts to them. - type: Fix message: Fixed fire being able to destroy your chest. - type: Fix @@ -8260,7 +8260,7 @@ Entries: - type: Tweak message: >- Unbound shitmed targeting doll keybinds by default (did you know we have - those). + those). id: 6546 time: '2024-11-30T16:06:56.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1271 @@ -8270,7 +8270,7 @@ Entries: message: >- All engineering roles have had their equipment loadouts significantly expanded upon. Engineers can now buy construction materials with their - loadout points. + loadout points. - type: Fix message: All engineering jobs now have their Suit/Skirt selection via loadouts. - type: Add @@ -8289,7 +8289,7 @@ Entries: - type: Add message: >- Added various articles of religious headgear to loadouts, such as Hijab, - Kippah, and Turban. All of these are set to allow custom colors. + Kippah, and Turban. All of these are set to allow custom colors. id: 6548 time: '2024-11-30T18:52:35.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1297 @@ -8372,7 +8372,7 @@ Entries: - type: Fix message: >- Enabled the "Mend brain tissue" surgical procedure on a lobotomized - target. + target. - type: Fix message: >- The lobotomized effect is now stored in the brain instead of the body. @@ -8505,7 +8505,7 @@ Entries: message: >- Examining the damage values of an item now shows its throwing damage, throwing stamina cost, whether or not it embeds on a throw, and if the - embed deals damage over time. + embed deals damage over time. - type: Add message: Examining an embedded item now shows what body part it's embedded in. - type: Tweak @@ -8661,7 +8661,7 @@ Entries: - type: Add message: >- Added a variety of Pointy Ears markings for Harpy and Arachne - characters. + characters. id: 6578 time: '2024-12-15T19:13:50.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1348 @@ -8729,7 +8729,7 @@ Entries: - type: Remove message: >- Removed all 'specific color' variants of colorable items from Loadouts, - such as "Blue Jumpsuit" when a colorable jumpsuit exists. + such as "Blue Jumpsuit" when a colorable jumpsuit exists. id: 6585 time: '2024-12-21T18:02:28.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1364 @@ -8837,7 +8837,7 @@ Entries: message: >- Tweaked arrivals to reduce the playable area, and some concessions have been made to prevent access to admeme items that were on display. These - changes are pending a later update where we port Arrivals Job Spawns. + changes are pending a later update where we port Arrivals Job Spawns. id: 6597 time: '2024-12-30T21:15:16.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1371 @@ -9159,211 +9159,3 @@ Entries: id: 6629 time: '2025-01-05T16:01:33.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1427 -- author: Skubman - changes: - - type: Add - message: >- - Lamiae can now be male, no longer being a female-only species. They can - now wear facial hair as well. - - type: Add - message: >- - The Lamia species can now select markings above the leg, including - cybernetics, makeup, tattoos, noses, earrings, heterochromia, bracelets, - reptilian horns and frills, pointy ears, and more! - - type: Fix - message: Added text for the Lamia tail marking. - id: 6630 - time: '2025-01-05T18:09:37.0000000+00:00' - url: https://github.com/Simple-Station/Einstein-Engines/pull/1440 -- author: Skubman - changes: - - type: Fix - message: >- - Fixed embeddable damage over time not showing when examining an item's - damage values. - - type: Fix - message: 'Fixed the emag not dealing passive damage when embedded on a target. ' - - type: Tweak - message: >- - Examining an item's damage values now shows values in a sorted order. - From top to bottom: gun damage, melee damage, throwing damage, embedded - damage. - - type: Tweak - message: >- - Spears now have a 6 stamina cost when thrown, and they no longer break - when thrown enough times. - - type: Tweak - message: Floor tiles now break when thrown 10 times instead of 4 times. - - type: Tweak - message: >- - Closets, lockers and crates now play the proper sound effect when thrown - (by Space Wind). - id: 6631 - time: '2025-01-05T18:10:29.0000000+00:00' - url: https://github.com/Simple-Station/Einstein-Engines/pull/1434 -- author: Skubman - changes: - - type: Add - message: >- - Added two new traits: Steadfast and Feeble that decrease or increase the - effect of slows from injuries. - - type: Tweak - message: >- - Injury slows for most species has been slightly increased, but this can - be mitigated with the Steadfast trait. - - type: Tweak - message: >- - The jackboots' injury slow resistance has been decreased from 50% to - 20%. - - type: Tweak - message: Combat boots now add resistance to injury slows like jackboots. - id: 6632 - time: '2025-01-05T18:24:29.0000000+00:00' - url: https://github.com/Simple-Station/Einstein-Engines/pull/1431 -- author: VMSolidus - changes: - - type: Add - message: >- - Footprints are now cleaned in a small radius around the mouse cursor - when mopping them. - id: 6633 - time: '2025-01-05T18:27:12.0000000+00:00' - url: https://github.com/Simple-Station/Einstein-Engines/pull/1439 -- author: Skubman - changes: - - type: Fix - message: >- - Pacified characters (Pacifist trait and thieves) can no longer throw - items that deal throwing damage. - id: 6634 - time: '2025-01-05T18:30:15.0000000+00:00' - url: https://github.com/Simple-Station/Einstein-Engines/pull/1433 -- author: Skubman - changes: - - type: Tweak - message: >- - The Parkour Training trait has been reworked. It no longer makes you lie - down or get up faster, but grants you 30% shorter slip stuns and 50% - resistance against slows in difficult terrain. - - type: Tweak - message: >- - The Sluggish and Snail-Paced traits now only reduce your movement speed - with no other effects. The speed reductions have been slightly increased - from 15% to 16% for Sluggish, and 30% to 32% for Snail-Paced. - - type: Add - message: >- - Added a new 3-point negative trait called Bad Knees. It is the opposite - of Parkour Training and it makes you climb tables slower, stunned for - longer against slip stuns, and move more slowly in difficult terrain. - - type: Tweak - message: Snail-Paced no longer prevents you from slipping when running. - id: 6635 - time: '2025-01-05T18:31:21.0000000+00:00' - url: https://github.com/Simple-Station/Einstein-Engines/pull/1432 -- author: Skubman - changes: - - type: Fix - message: >- - The Face marking category now has unlimited markings for all species - apart from IPCs. - - type: Fix - message: >- - Moth People can now apply Moth makeup markings again in the Face - category. - - type: Tweak - message: >- - Unathi now have 3 'Head (Side)' marking points available, so they can - use frills and both left and right-side earrings. - id: 6636 - time: '2025-01-05T18:35:46.0000000+00:00' - url: https://github.com/Simple-Station/Einstein-Engines/pull/1435 -- author: VMSolidus - changes: - - type: Fix - message: >- - Lamia now make drag marks when moving through puddles instead of regular - footprints. - id: 6637 - time: '2025-01-05T18:37:43.0000000+00:00' - url: https://github.com/Simple-Station/Einstein-Engines/pull/1425 -- author: VMSolidus - changes: - - type: Fix - message: 'Confiscated Pun Pun''s blackface. ' - id: 6638 - time: '2025-01-05T19:21:08.0000000+00:00' - url: https://github.com/Simple-Station/Einstein-Engines/pull/1437 -- author: VMSolidus - changes: - - type: Tweak - message: 'Mimes can no longer take the Muted or Foreigner traits. ' - - type: Tweak - message: >- - "Rare" languages like Sinta'Azaziba, Valyrian Standard, now cost points - instead of giving points. - - type: Tweak - message: Sign Language now costs points. - id: 6639 - time: '2025-01-05T19:45:18.0000000+00:00' - url: https://github.com/Simple-Station/Einstein-Engines/pull/1441 -- author: VMSolidus - changes: - - type: Add - message: Ported a ton of new clothing items from Frontier - id: 6640 - time: '2025-01-05T19:45:36.0000000+00:00' - url: https://github.com/Simple-Station/Einstein-Engines/pull/1388 -- author: VMSolidus - changes: - - type: Fix - message: Fixed the Syndicate Listening Post not correctly spawning. - id: 6641 - time: '2025-01-05T19:45:57.0000000+00:00' - url: https://github.com/Simple-Station/Einstein-Engines/pull/1438 -- author: The Den Contributors - changes: - - type: Fix - message: >- - Put actual prescription lenses in the prescription medhuds and sechuds - (by KyuPolaris) - - type: Add - message: >- - Added the CMO turtleneck and head mirror to the CMO loadouts. (by - sleepyyapril, KyuPolaris) - - type: Add - message: >- - Added an armored trenchcoat for the Captain. Enjoy your swag! (by - Rosycup) - - type: Fix - message: Fixed uneven pants leg on summer security uniforms. (by Rosycup) - - type: Add - message: >- - Added the Captain's Combat Gas Mask, for those more fond of close - encounters. (by Rosycup) - - type: Tweak - message: >- - Added the Captain's Trenchcoat to the other captain locker variants. (by - Rosycup) - - type: Fix - message: >- - Fixed they/them pronouns being displayed for it/its characters in the - character preview. (by Azzy) - id: 6642 - time: '2025-01-05T20:46:36.0000000+00:00' - url: https://github.com/Simple-Station/Einstein-Engines/pull/1443 -- author: TwoDucksOnnaPlane - changes: - - type: Fix - message: >- - Fixed Syndicate Elite hardsuit being one singular Telecrystal. Cybersun - will never recover from this financial blunder. - id: 6643 - time: '2025-01-07T01:21:34.0000000+00:00' - url: https://github.com/Simple-Station/Einstein-Engines/pull/1449 -- author: Skubman - changes: - - type: Tweak - message: The maximum trait limit has increased from 10 traits to 14 traits. - id: 6644 - time: '2025-01-07T04:08:32.0000000+00:00' - url: https://github.com/Simple-Station/Einstein-Engines/pull/1446 diff --git a/Resources/Locale/en-US/damage/stamina.ftl b/Resources/Locale/en-US/damage/stamina.ftl index 777101f93e..09f9164497 100644 --- a/Resources/Locale/en-US/damage/stamina.ftl +++ b/Resources/Locale/en-US/damage/stamina.ftl @@ -1,5 +1,5 @@ melee-stamina = Not enough stamina -slow-on-damage-modifier-examine = Slowness from injuries is reduced by [color=yellow]{$mod}%[/color]. +slow-on-damage-modifier-examine = Slowness from injuries is reduced by [color=yellow]{$mod}%[/color] throw-no-stamina = You don't have enough stamina to throw the {$item}! diff --git a/Resources/Locale/en-US/markings/lamia.ftl b/Resources/Locale/en-US/markings/lamia.ftl deleted file mode 100644 index 6e32e2fe36..0000000000 --- a/Resources/Locale/en-US/markings/lamia.ftl +++ /dev/null @@ -1,4 +0,0 @@ -marking-LamiaBottom = Lamia Tail -marking-LamiaBottom-bottom3tone1 = Lamia Tail Bottom -marking-LamiaBottom-bottom3tone2 = Lamia Tail Middle -marking-LamiaBottom-bottom3tone3 = Lamia Tail Top diff --git a/Resources/Locale/en-US/preferences/humanoid-character-profile.ftl b/Resources/Locale/en-US/preferences/humanoid-character-profile.ftl index 9058f8d2b6..800da35ba5 100644 --- a/Resources/Locale/en-US/preferences/humanoid-character-profile.ftl +++ b/Resources/Locale/en-US/preferences/humanoid-character-profile.ftl @@ -5,6 +5,5 @@ humanoid-character-profile-summary = This is {$name}. {$gender -> [male] He is [female] She is - [neuter] It is *[other] They are -} {$age} years old. +} {$age} years old. \ No newline at end of file diff --git a/Resources/Locale/en-US/traits/traits.ftl b/Resources/Locale/en-US/traits/traits.ftl index 37de6238d7..25188ca851 100644 --- a/Resources/Locale/en-US/traits/traits.ftl +++ b/Resources/Locale/en-US/traits/traits.ftl @@ -163,20 +163,6 @@ trait-description-LowPainTolerance = Your tolerance for pain is far below average, and its effects are more inhibiting. Your melee/throwing damage is penalized by up to an additional 15% when taking stamina damage. -trait-name-Steadfast = Steadfast -trait-description-Steadfast = - When others would buckle from the weight of your injuries, you still march forward unrelentingly. - For most species [color=gray](excluding IPC/Shadowkin)[/color], this trait modifies: - - [color=yellow]25%[/color] movement slow at [color=red]60[/color] damage ➔ [color=yellow]17%[/color] movement slow at [color=red]70[/color] damage - - [color=yellow]45%[/color] movement slow at [color=red]80[/color] damage ➔ [color=yellow]30%[/color] movement slow at [color=red]90[/color] damage - -trait-name-Feeble = Feeble -trait-description-Feeble = - Your body responds poorly to injuries, making damage affect your movement more severely. - For most species [color=gray](excluding IPC/Shadowkin)[/color], this trait modifies: - - [color=yellow]25%[/color] movement slow at [color=red]60[/color] damage ➔ [color=yellow]30%[/color] movement slow at [color=red]45[/color] damage - - [color=yellow]45%[/color] movement slow at [color=red]80[/color] damage ➔ [color=yellow]54%[/color] movement slow at [color=red]65[/color] damage - trait-name-MartialArtist = Martial Artist trait-description-MartialArtist = You have received formal training in unarmed combat, whether with Fists, Feet, or Claws. @@ -224,32 +210,20 @@ trait-description-Voracious = Nothing gets between you and your food. Your endless consumption of food and drinks is twice as fast. --terrain-example = [color=gray](e.g. spider web, slime puddle, kudzu, space glue)[/color] --slippery-example = [color=gray](e.g. banana peel, water puddle, soap, space lube)[/color] - trait-name-ParkourTraining = Parkour Training trait-description-ParkourTraining = Whether as a hobby, lifestyle, or professional training, you are trained in the discipline of parkour. - You climb structures like tables [color=yellow]50%[/color] faster. - Slipping leaves you stunned for [color=yellow]30%[/color] shorter. { -slippery-example } - You gain a [color=yellow]50%[/color] resistance to slows from difficult terrain. { -terrain-example } - -trait-name-BadKnees = Bad Knees -trait-description-BadKnees = - Whether due to injury, age, or wear and tear, your knees aren't particularly strong or flexible. - You climb structures like tables [color=yellow]50%[/color] slower. - Slipping leaves you stunned for [color=yellow]40%[/color] longer. { -slippery-example } - Difficult terrain slows you down [color=yellow]35%[/color] more. { -terrain-example } + You're faster with climbing, crawling, lying down, and getting up. trait-name-Sluggish = Sluggish trait-description-Sluggish = You navigate the world slower than others, perhaps due to a medical condition, inactivity, or age. - Your movement speed is decreased by [color=yellow]16%[/color]. + You move slower, and it takes longer for you to climb, lie down and get up. trait-name-SnailPaced = Snail-Paced trait-description-SnailPaced = You walk at a snail's pace, perhaps due to a medical condition, mobility impairment, or age. - Your movement speed is decreased by [color=yellow]32%[/color]. + You move substantially slower, and it takes far longer for you to climb, lie down and get up. trait-name-LightStep = Light Step trait-description-LightStep = diff --git a/Resources/Prototypes/Catalog/Cargo/cargo_vending.yml b/Resources/Prototypes/Catalog/Cargo/cargo_vending.yml index 8ff4a819ee..937ee63b50 100644 --- a/Resources/Prototypes/Catalog/Cargo/cargo_vending.yml +++ b/Resources/Prototypes/Catalog/Cargo/cargo_vending.yml @@ -43,7 +43,7 @@ sprite: Objects/Specific/Service/vending_machine_restock.rsi state: base product: CrateVendingMachineRestockAutoDrobeFilled - cost: 3500 + cost: 1730 category: cargoproduct-category-name-service group: market diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/dressers.yml b/Resources/Prototypes/Catalog/Fills/Lockers/dressers.yml index b1a9271158..94e10d76ee 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/dressers.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/dressers.yml @@ -39,10 +39,7 @@ - id: ClothingNeckMantleCMO - id: ClothingCloakCmo - id: ClothingOuterCoatLabCmo - - id: ClothingUniformJumpsuitCMOTurtle - - id: ClothingUniformJumpskirtCMOTurtle - id: ClothingHeadHatBeretCmo - - id: ClothingHeadMirror - id: ClothingEyesGlasses - id: ClothingOuterWinterCMO diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml b/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml index 0bce0f2fb8..fec4f3ffcf 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml @@ -33,7 +33,6 @@ components: - type: StorageFill contents: - - id: ClothingOuterCoatCapTrench - id: NukeDisk - id: PinpointerNuclear - id: CaptainIDCard @@ -55,7 +54,6 @@ components: - type: StorageFill contents: - - id: ClothingOuterCoatCapTrench - id: NukeDisk - id: PinpointerNuclear - id: CaptainIDCard @@ -76,7 +74,6 @@ components: - type: StorageFill contents: - - id: ClothingOuterCoatCapTrench - id: NukeDisk - id: PinpointerNuclear - id: CaptainIDCard diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/medical.yml b/Resources/Prototypes/Catalog/Fills/Lockers/medical.yml index e7a367c454..f217791683 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/medical.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/medical.yml @@ -45,8 +45,6 @@ contents: - id: HandheldHealthAnalyzer prob: 0.6 - - id: ClothingHeadMirror - prob: 0.1 - id: ClothingHandsGlovesLatex - id: ClothingHeadsetMedical - id: ClothingEyesHudMedical diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/suit_storage.yml b/Resources/Prototypes/Catalog/Fills/Lockers/suit_storage.yml index 94ef753c78..734bd485a2 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/suit_storage.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/suit_storage.yml @@ -228,7 +228,6 @@ - id: OxygenTankFilled - id: ClothingOuterHardsuitCap - id: ClothingMaskGasCaptain - - id: ClothingMaskGasCaptainCombat - type: AccessReader access: [["Captain"]] diff --git a/Resources/Prototypes/Catalog/VendingMachines/Inventories/chapel.yml b/Resources/Prototypes/Catalog/VendingMachines/Inventories/chapel.yml index 21c0eb011e..6f1b813a8f 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Inventories/chapel.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Inventories/chapel.yml @@ -1,26 +1,21 @@ - type: vendingMachineInventory id: PietyVendInventory startingInventory: - ClothingUniformJumpsuitChaplain: 3 - ClothingUniformJumpskirtChaplain: 3 - ClothingUniformJumpsuitMonasticRobeDark: 3 - ClothingUniformJumpsuitMonasticRobeLight: 3 - ClothingOuterHoodieChaplain: 3 - ClothingOuterHoodieBlack: 3 - ClothingHeadHatHoodNunHood: 3 - ClothingOuterNunRobe: 3 - ClothingHeadHatFez: 3 - ClothingHeadHatPlaguedoctor: 3 - ClothingHeadHatWitch: 3 - ClothingHeadHatWitch1: 3 - ClothingOuterPlagueSuit: 3 - ClothingMaskPlague: 3 - ClothingNeckScarfChaplainStole: 3 - ClothingHeadsetService: 4 - RubberStampChaplain: 3 - Bible: 3 - ClothingNeckCrucifix: 3 + ClothingUniformJumpsuitChaplain: 2 + ClothingUniformJumpskirtChaplain: 2 + ClothingUniformJumpsuitMonasticRobeDark: 1 + ClothingUniformJumpsuitMonasticRobeLight: 1 ClothingNeckStoleChaplain: 1 + ClothingOuterHoodieChaplain: 1 + ClothingOuterHoodieBlack: 1 + ClothingHeadHatHoodNunHood: 1 + ClothingOuterNunRobe: 1 + ClothingHeadHatFez: 1 + ClothingHeadHatPlaguedoctor: 1 + ClothingHeadHatWitch: 1 + ClothingHeadHatWitch1: 1 + ClothingOuterPlagueSuit: 1 + ClothingMaskPlague: 1 #ClothingHeadsetService: 2 # Delta-V - Chaplain is no longer service dept BoxCandle: 2 BoxCandleSmall: 2 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Inventories/theater.yml b/Resources/Prototypes/Catalog/VendingMachines/Inventories/theater.yml index ff8b4087ab..9a1566dab9 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Inventories/theater.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Inventories/theater.yml @@ -88,50 +88,6 @@ ClothingShoesBootsCowboyBlack: 1 ClothingShoesBootsCowboyWhite: 1 ClothingMaskNeckGaiterRed: 2 - ClothingUniformJumpskirtPerformer: 2 - ClothingShoesBootsPerformer: 2 - ClothingNeckCloakMoth: 2 - ClothingUniformJumpskirtJanimaid: 2 - ClothingNeckCloakVoid: 2 - ClothingHeadBandRed: 1 - ClothingHeadHatPirate: 1 - ClothingOuterCoatPirate: 1 - ClothingUniformJumpsuitPirate: 1 - ClothingShoesBootsLaceup: 2 - FoamCutlass: 2 - ClothingHeadSafari: 2 # Frontier - ClothingUniformJumpsuitSafari: 2 # Frontier - ClothingUniformRandomArmlessNoSkirt: 6 # Frontier - ClothingUniformRandomArmless: 6 # Frontier - ClothingUniformRandomStandard: 6 # Frontier - ClothingUniformRandomBra: 6 # Frontier - ClothingUniformRandomShorts: 6 # Frontier - ClothingUniformRandomShirt: 6 # Frontier - ClothingHandsGlovesBoxingRed: 2 - ClothingHandsGlovesBoxingBlue: 2 - ClothingHandsGlovesBoxingYellow: 2 - ClothingHandsGlovesBoxingGreen: 2 - UniformShortsRed: 4 - UniformShortsRedWithTop: 4 - ClothingBackpackClownFilled: 2 - ClothingBackpackSatchelClown: 2 - ClothingBackpackDuffelClown: 2 - ClothingMaskClown: 2 - ClothingShoesClown: 2 - ClothingUniformJumpsuitClown: 2 - RubberStampClown: 1 - ClothingBackpackMime: 2 - ClothingBackpackSatchelMime: 2 - ClothingBackpackDuffelMime: 2 - ClothingHeadHatMimesoft: 2 - ClothingMaskMime: 2 - ClothingUniformJumpsuitMime: 2 - ClothingUniformJumpskirtMime: 2 - ClothingBeltSuspenders: 2 - RubberStampMime: 1 - contrabandInventory: # Frontier - Hidden inventory - ClothingHeadHatMimesoftFlipped: 1 - BarberScissors: 1 ClothingNeckBellCollar: 2 ClothingOuterUnathiRobe: 1 emaggedInventory: diff --git a/Resources/Prototypes/Catalog/uplink_catalog.yml b/Resources/Prototypes/Catalog/uplink_catalog.yml index 7f9088a3d0..0bc47d74eb 100644 --- a/Resources/Prototypes/Catalog/uplink_catalog.yml +++ b/Resources/Prototypes/Catalog/uplink_catalog.yml @@ -1495,7 +1495,7 @@ discountDownTo: Telecrystal: 7 cost: - Telecrystal: 12 + Telecrystal: 1 categories: - UplinkWearables @@ -1850,4 +1850,4 @@ cost: Telecrystal: 1 categories: - - UplinkWearables + - UplinkWearables \ No newline at end of file diff --git a/Resources/Prototypes/CharacterItemGroups/Generic/outerwearGroup.yml b/Resources/Prototypes/CharacterItemGroups/Generic/outerwearGroup.yml index 033a52c6bd..50eb4dc8be 100644 --- a/Resources/Prototypes/CharacterItemGroups/Generic/outerwearGroup.yml +++ b/Resources/Prototypes/CharacterItemGroups/Generic/outerwearGroup.yml @@ -69,31 +69,3 @@ id: LoadoutOuterCoatInspector - type: loadout id: LoadoutOuterCoatOvercoat - - type: loadout - id: LoadoutOuterBomberAerostatic - - type: loadout - id: LoadoutOuterBomberPunkRed - - type: loadout - id: LoadingOuterCoatJacketLeather - - type: loadout - id: LoadoutOuterJacketBiker - - type: loadout - id: LoadoutOuterJacketJamrock - - type: loadout - id: LoadoutOuterJacketLettermanBlue - - type: loadout - id: LoadoutOuterJacketLettermanBrown - - type: loadout - id: LoadoutOuterJacketLettermanMagenta - - type: loadout - id: LoadoutOuterJacketLettermanRed - - type: loadout - id: LoadoutOuterCoatAcidRaincoat - - type: loadout - id: LoadoutOuterCoatAcidRaincoatBlue - - type: loadout - id: LoadoutOuterCoatAcidRaincoatGreen - - type: loadout - id: LoadoutOuterCoatAcidRaincoatMagenta - - type: loadout - id: LoadoutOuterCoatAcidRaincoatYellow diff --git a/Resources/Prototypes/CharacterItemGroups/Jobs/Command/captain.yml b/Resources/Prototypes/CharacterItemGroups/Jobs/Command/captain.yml index 842c285909..57df945ec9 100644 --- a/Resources/Prototypes/CharacterItemGroups/Jobs/Command/captain.yml +++ b/Resources/Prototypes/CharacterItemGroups/Jobs/Command/captain.yml @@ -107,8 +107,6 @@ items: - type: loadout id: LoadoutCommandCapMaskGas - - type: loadout - id: LoadoutCommandCapMaskGasCombat - type: characterItemGroup id: LoadoutCaptainOuter @@ -118,8 +116,6 @@ id: LoadoutCommandCapOuterWinter - type: loadout id: LoadoutCaptainOuterCarapace - - type: loadout - id: LoadoutCaptainOuterTrench - type: characterItemGroup id: LoadoutCaptainShoes diff --git a/Resources/Prototypes/CharacterItemGroups/Jobs/Medical/chiefMedicalOfficer.yml b/Resources/Prototypes/CharacterItemGroups/Jobs/Medical/chiefMedicalOfficer.yml index 77f6b3f26e..6e9bd02b4f 100644 --- a/Resources/Prototypes/CharacterItemGroups/Jobs/Medical/chiefMedicalOfficer.yml +++ b/Resources/Prototypes/CharacterItemGroups/Jobs/Medical/chiefMedicalOfficer.yml @@ -46,8 +46,6 @@ items: - type: loadout id: LoadoutChiefMedicalOfficerNTPDA - - type: loadout - id: LoadoutClothingHeadMirror - type: characterItemGroup id: LoadoutChiefMedicalOfficerNeck @@ -93,7 +91,3 @@ id: LoadoutChiefMedicalOfficerJumpsuit - type: loadout id: LoadoutChiefMedicalOfficerJumpskirt - - type: loadout - id: LoadoutChiefMedicalOfficerTurtleskirt - - type: loadout - id: LoadoutChiefMedicalOfficerTurtlesuit diff --git a/Resources/Prototypes/DeltaV/Entities/Clothing/Eyes/hud.yml b/Resources/Prototypes/DeltaV/Entities/Clothing/Eyes/hud.yml index f11cb930f0..1e8a64b0df 100644 --- a/Resources/Prototypes/DeltaV/Entities/Clothing/Eyes/hud.yml +++ b/Resources/Prototypes/DeltaV/Entities/Clothing/Eyes/hud.yml @@ -11,7 +11,6 @@ - type: Construction graph: PrescriptionMedHud node: prescmedhud - - type: VisionCorrection - type: ShowHealthBars damageContainers: - Biological @@ -32,7 +31,6 @@ sprite: DeltaV/Clothing/Eyes/Hud/prescsechud.rsi - type: Clothing sprite: DeltaV/Clothing/Eyes/Hud/prescsechud.rsi - - type: VisionCorrection - type: Construction graph: PrescriptionSecHud node: prescsechud diff --git a/Resources/Prototypes/DeltaV/Entities/Mobs/Species/lamia.yml b/Resources/Prototypes/DeltaV/Entities/Mobs/Species/lamia.yml index 9d7a85f97d..38601f21b5 100644 --- a/Resources/Prototypes/DeltaV/Entities/Mobs/Species/lamia.yml +++ b/Resources/Prototypes/DeltaV/Entities/Mobs/Species/lamia.yml @@ -106,19 +106,14 @@ - map: [ "belt2" ] - map: [ "neck" ] - map: [ "back" ] - - map: [ "enum.HumanoidVisualLayers.Face" ] - - map: [ "enum.HumanoidVisualLayers.FacialHair" ] - map: [ "enum.HumanoidVisualLayers.Hair" ] - - map: [ "enum.HumanoidVisualLayers.HeadSide" ] - - map: [ "enum.HumanoidVisualLayers.HeadTop" ] + state: bald + sprite: Mobs/Customization/human_hair.rsi - map: [ "mask" ] - map: [ "head" ] - map: [ "pocket1" ] - map: [ "pocket2" ] - - map: [ "clownedon" ] # Dynamically generated - sprite: "Effects/creampie.rsi" - state: "creampie_human" - visible: false + - map: [ "enum.HumanoidVisualLayers.HeadTop" ] - type: Damageable damageContainer: Biological damageModifierSet: Scale #TODO: make a new damage modifier set @@ -221,11 +216,6 @@ types: Bloodloss: -1 - type: PortalExempt - - type: FootPrints - leftBarePrint: "dragging-1" - rightBarePrint: "dragging-1" - shoesPrint: "dragging-1" - suitPrint: "dragging-1" - type: entity save: false @@ -283,15 +273,14 @@ - map: [ "belt" ] - map: [ "neck" ] - map: [ "back" ] - - map: [ "enum.HumanoidVisualLayers.Face" ] - - map: [ "enum.HumanoidVisualLayers.FacialHair" ] - map: [ "enum.HumanoidVisualLayers.Hair" ] - - map: [ "enum.HumanoidVisualLayers.HeadSide" ] - - map: [ "enum.HumanoidVisualLayers.HeadTop" ] + state: bald + sprite: Mobs/Customization/human_hair.rsi - map: [ "mask" ] - map: [ "head" ] - map: [ "pocket1" ] - map: [ "pocket2" ] + - map: [ "enum.HumanoidVisualLayers.HeadTop" ] - map: [ "enum.HumanoidVisualLayers.Tail" ] - type: Inventory templateId: lamia diff --git a/Resources/Prototypes/DeltaV/Species/lamia.yml b/Resources/Prototypes/DeltaV/Species/lamia.yml index 8e149b9dbe..bdd015ade0 100644 --- a/Resources/Prototypes/DeltaV/Species/lamia.yml +++ b/Resources/Prototypes/DeltaV/Species/lamia.yml @@ -10,6 +10,8 @@ maleFirstNames: names_cyno_male femaleFirstNames: names_cyno_female lastNames: names_cyno_last + sexes: + - Female - type: markingPoints id: MobLamiaMarkingLimits @@ -17,49 +19,17 @@ Hair: points: 1 required: false - FacialHair: - points: 1 - required: false - Snout: - points: 1 - required: false - HeadTop: - points: 2 - required: false - HeadSide: - points: 3 - required: false Tail: points: 1 required: true defaultMarkings: [ LamiaBottom ] - Chest: - points: 1 - required: false - RightArm: - points: 2 - required: false - RightHand: - points: 3 - required: false - LeftArm: - points: 2 - required: false - LeftHand: - points: 3 - required: false - type: speciesBaseSprites id: MobLamiaSprites sprites: Head: MobHumanHead - Face: MobHumanoidAnyMarking - HeadTop: MobHumanoidAnyMarking - HeadSide: MobHumanoidAnyMarking Hair: MobHumanoidAnyMarking - FacialHair: MobHumanoidAnyMarking - Snout: MobHumanoidAnyMarking Chest: MobHumanTorso Eyes: MobHumanoidEyes LArm: MobHumanLArm diff --git a/Resources/Prototypes/DeltaV/Species/vulpkanin.yml b/Resources/Prototypes/DeltaV/Species/vulpkanin.yml index 0ad6519c76..4c276a3346 100644 --- a/Resources/Prototypes/DeltaV/Species/vulpkanin.yml +++ b/Resources/Prototypes/DeltaV/Species/vulpkanin.yml @@ -37,6 +37,9 @@ - type: markingPoints id: MobVulpkaninMarkingLimits points: + Face: + points: 1 + required: false Hair: points: 1 required: false diff --git a/Resources/Prototypes/Entities/Clothing/Head/misc.yml b/Resources/Prototypes/Entities/Clothing/Head/misc.yml index 0138d8aee4..bd7899d75f 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/misc.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/misc.yml @@ -86,17 +86,6 @@ - type: Clothing sprite: Clothing/Head/Misc/pwig.rsi -- type: entity - parent: ClothingHeadBase - id: ClothingHeadMirror - name: head mirror - description: I doubt even the CMO knows how to use this thing. - components: - - type: Sprite - sprite: Clothing/Head/Misc/head_mirror.rsi - - type: Clothing - sprite: Clothing/Head/Misc/head_mirror.rsi - - type: entity parent: ClothingHeadBase id: ClothingHeadHatRichard diff --git a/Resources/Prototypes/Entities/Clothing/Masks/masks.yml b/Resources/Prototypes/Entities/Clothing/Masks/masks.yml index c5b34a7a67..383e32e99d 100644 --- a/Resources/Prototypes/Entities/Clothing/Masks/masks.yml +++ b/Resources/Prototypes/Entities/Clothing/Masks/masks.yml @@ -88,17 +88,6 @@ - type: BreathMask - type: IngestionBlocker -- type: entity - parent: ClothingMaskGasExplorer - id: ClothingMaskGasCaptainCombat - name: captain's combat gas mask - description: A military-grade gas mask that can be connected to an air supply, painted and outfitted with an emblem befitting its wearer. Issued only to the station's finest. - components: - - type: Sprite - sprite: Clothing/Mask/gascaptaincombat.rsi - - type: Clothing - sprite: Clothing/Mask/gascaptaincombat.rsi - - type: entity parent: ClothingMaskGasAtmos id: ClothingMaskGasCentcom diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml index 07fe385478..dc5454d597 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml @@ -51,17 +51,6 @@ - type: Clothing sprite: Clothing/OuterClothing/Coats/gentlecoat.rsi -- type: entity - parent: [ClothingOuterArmorCaptainCarapace, ClothingOuterStorageBase] - id: ClothingOuterCoatCapTrench - name: captain's armored trenchcoat - description: A greatcoat enhanced with a special alloy for some extra protection and style for those with a commanding presence, outfitted with emblems and decor befitting its wearer. Issued only to the station's finest. - components: - - type: Sprite - sprite: Clothing/OuterClothing/Coats/cap_trenchcoat.rsi - - type: Clothing - sprite: Clothing/OuterClothing/Coats/cap_trenchcoat.rsi - - type: entity abstract: true parent: AllowSuitStorageClothing diff --git a/Resources/Prototypes/Entities/Clothing/Shoes/boots.yml b/Resources/Prototypes/Entities/Clothing/Shoes/boots.yml index 01576a1f93..069555c836 100644 --- a/Resources/Prototypes/Entities/Clothing/Shoes/boots.yml +++ b/Resources/Prototypes/Entities/Clothing/Shoes/boots.yml @@ -21,7 +21,7 @@ - type: Clothing sprite: Clothing/Shoes/Boots/jackboots.rsi - type: ClothingSlowOnDamageModifier - modifier: 0.8 + modifier: 0.5 - type: entity parent: ClothingShoesMilitaryBase @@ -67,8 +67,6 @@ sprite: Clothing/Shoes/Boots/combatboots.rsi - type: Clothing sprite: Clothing/Shoes/Boots/combatboots.rsi - - type: ClothingSlowOnDamageModifier - modifier: 0.8 - type: entity parent: ClothingShoesMilitaryBase @@ -95,8 +93,6 @@ sprite: Clothing/Shoes/Boots/mercboots.rsi - type: Clothing sprite: Clothing/Shoes/Boots/mercboots.rsi - - type: ClothingSlowOnDamageModifier - modifier: 0.6 - type: entity parent: ClothingShoesBaseButcherable diff --git a/Resources/Prototypes/Entities/Clothing/Shoes/specific.yml b/Resources/Prototypes/Entities/Clothing/Shoes/specific.yml index e9ea123928..3ec17e2bbe 100644 --- a/Resources/Prototypes/Entities/Clothing/Shoes/specific.yml +++ b/Resources/Prototypes/Entities/Clothing/Shoes/specific.yml @@ -137,7 +137,7 @@ - type: Clothing sprite: Clothing/Shoes/Specific/swat.rsi - type: ClothingSlowOnDamageModifier - modifier: 0.7 + modifier: 0.5 - type: entity parent: ClothingShoesBaseButcherable diff --git a/Resources/Prototypes/Entities/Clothing/Uniforms/jumpskirts.yml b/Resources/Prototypes/Entities/Clothing/Uniforms/jumpskirts.yml index 7f6d59c9a4..18266dc498 100644 --- a/Resources/Prototypes/Entities/Clothing/Uniforms/jumpskirts.yml +++ b/Resources/Prototypes/Entities/Clothing/Uniforms/jumpskirts.yml @@ -132,17 +132,6 @@ - type: Clothing sprite: Clothing/Uniforms/Jumpskirt/cmo.rsi -- type: entity - parent: ClothingUniformSkirtBase - id: ClothingUniformJumpskirtCMOTurtle - name: chief medical officer's turtleneck jumpskirt - description: It's a turtleneck worn by those with the experience to be Chief Medical Officer. It provides minor biological protection. - components: - - type: Sprite - sprite: Clothing/Uniforms/Jumpskirt/cmo_turtle.rsi - - type: Clothing - sprite: Clothing/Uniforms/Jumpskirt/cmo_turtle.rsi - - type: entity parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtDetective diff --git a/Resources/Prototypes/Entities/Clothing/Uniforms/jumpsuits.yml b/Resources/Prototypes/Entities/Clothing/Uniforms/jumpsuits.yml index 3ada362ce0..d2339764f8 100644 --- a/Resources/Prototypes/Entities/Clothing/Uniforms/jumpsuits.yml +++ b/Resources/Prototypes/Entities/Clothing/Uniforms/jumpsuits.yml @@ -350,17 +350,6 @@ - type: Clothing sprite: Clothing/Uniforms/Jumpsuit/cmo.rsi -- type: entity - parent: ClothingUniformBase - id: ClothingUniformJumpsuitCMOTurtle - name: chief medical officer's turtleneck jumpsuit - description: It's a turtleneck worn by those with the experience to be Chief Medical Officer. It provides minor biological protection. - components: - - type: Sprite - sprite: Clothing/Uniforms/Jumpsuit/cmo_turtle.rsi - - type: Clothing - sprite: Clothing/Uniforms/Jumpsuit/cmo_turtle.rsi - - type: entity parent: ClothingUniformBase id: ClothingUniformJumpsuitDetective diff --git a/Resources/Prototypes/Entities/Effects/puddle.yml b/Resources/Prototypes/Entities/Effects/puddle.yml index 988b06de22..7f6125e73d 100644 --- a/Resources/Prototypes/Entities/Effects/puddle.yml +++ b/Resources/Prototypes/Entities/Effects/puddle.yml @@ -205,13 +205,3 @@ - type: Puddle solution: step - type: Appearance - - type: Drink - delay: 3 - transferAmount: 1 - solution: step - examinable: false - - type: ExaminableSolution - solution: step - - type: DrawableSolution - solution: step - - type: BadDrink diff --git a/Resources/Prototypes/Entities/Mobs/Customization/Markings/earrings.yml b/Resources/Prototypes/Entities/Mobs/Customization/Markings/earrings.yml index 77b0ba3d0e..cf98e96cff 100644 --- a/Resources/Prototypes/Entities/Mobs/Customization/Markings/earrings.yml +++ b/Resources/Prototypes/Entities/Mobs/Customization/Markings/earrings.yml @@ -2,7 +2,7 @@ id: EarringsStudLeft bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -16,7 +16,7 @@ id: EarringsStudRight bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -30,7 +30,7 @@ id: EarringsHeavyLeft bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -44,7 +44,7 @@ id: EarringsHeavyRight bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -58,7 +58,7 @@ id: EarringsDropBasicLeft bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -72,7 +72,7 @@ id: EarringsDropBasicRight bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -86,7 +86,7 @@ id: EarringsDropColoredLeft bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -102,7 +102,7 @@ id: EarringsDropColoredRight bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -118,7 +118,7 @@ id: EarringsDropLongLeft bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -134,7 +134,7 @@ id: EarringsDropLongRight bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -150,7 +150,7 @@ id: EarringsCrescentLeft bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -164,7 +164,7 @@ id: EarringsCrescentRight bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -178,7 +178,7 @@ id: EarringsBangleLeft bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -192,7 +192,7 @@ id: EarringsBangleRight bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -206,7 +206,7 @@ id: EarringsHoopBasicLeft bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -220,7 +220,7 @@ id: EarringsHoopBasicRight bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -234,7 +234,7 @@ id: EarringsHoopMiniLeft bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -248,7 +248,7 @@ id: EarringsHoopMiniRight bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -262,7 +262,7 @@ id: EarringsCrossBasicLeft bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -276,7 +276,7 @@ id: EarringsCrossBasicRight bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -290,7 +290,7 @@ id: EarringsCrossSaintPeterLeft bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -304,7 +304,7 @@ id: EarringsCrossSaintPeterRight bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -318,7 +318,7 @@ id: EarringsGemstoneBasicLeft bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -334,7 +334,7 @@ id: EarringsGemstoneBasicRight bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -350,7 +350,7 @@ id: EarringsGemstoneLongLeft bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -366,7 +366,7 @@ id: EarringsGemstoneLongRight bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -382,7 +382,7 @@ id: EarringsGemstoneDoubleLeft bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -400,7 +400,7 @@ id: EarringsGemstoneDoubleRight bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -418,7 +418,7 @@ id: EarringsDangleBasicLeft bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -434,7 +434,7 @@ id: EarringsDangleBasicRight bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -450,7 +450,7 @@ id: EarringsDangleLongLeft bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -466,7 +466,7 @@ id: EarringsDangleLongRight bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -482,7 +482,7 @@ id: EarringsEightLeft bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -496,7 +496,7 @@ id: EarringsEightRight bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -510,7 +510,7 @@ id: EarringsCrystalBasicLeft bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -526,7 +526,7 @@ id: EarringsCrystalBasicRight bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -542,7 +542,7 @@ id: EarringsCrystalLongLeft bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: @@ -558,7 +558,7 @@ id: EarringsCrystalLongRight bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Arachnid, Reptilian, Diona, Oni, Felinid, Vulpkanin, Harpy, Gingerbread, Arachne] coloring: default: type: diff --git a/Resources/Prototypes/Entities/Mobs/Customization/Markings/face.yml b/Resources/Prototypes/Entities/Mobs/Customization/Markings/face.yml index eb1723eb23..2276c0a237 100644 --- a/Resources/Prototypes/Entities/Mobs/Customization/Markings/face.yml +++ b/Resources/Prototypes/Entities/Mobs/Customization/Markings/face.yml @@ -2,7 +2,7 @@ id: FaceBindi bodyPart: Face markingCategory: Face - speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne] coloring: default: type: @@ -16,7 +16,7 @@ id: FaceFullblush bodyPart: Face markingCategory: Face - speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Vulpkanin, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Vulpkanin, Arachne] coloring: default: type: @@ -30,7 +30,7 @@ id: FaceCheekspotRight bodyPart: Face markingCategory: Face - speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne] coloring: default: type: @@ -44,7 +44,7 @@ id: FaceCheekspotLeft bodyPart: Face markingCategory: Face - speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne] coloring: default: type: @@ -58,7 +58,7 @@ id: FaceChesireRight bodyPart: Face markingCategory: Face - speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Arachne] coloring: default: type: @@ -72,7 +72,7 @@ id: FaceChesireLeft bodyPart: Face markingCategory: Face - speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Arachne] coloring: default: type: @@ -86,7 +86,7 @@ id: FaceCrowRight bodyPart: Face markingCategory: Face - speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne] coloring: default: type: @@ -100,7 +100,7 @@ id: FaceCrowLeft bodyPart: Face markingCategory: Face - speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne] coloring: default: type: @@ -114,7 +114,7 @@ id: FaceEarRight bodyPart: Face markingCategory: Face - speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Arachne] coloring: default: type: @@ -128,7 +128,7 @@ id: FaceEarLeft bodyPart: Face markingCategory: Face - speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Arachne] coloring: default: type: @@ -142,7 +142,7 @@ id: FaceEyebrowRight bodyPart: Face markingCategory: Face - speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne] coloring: default: type: @@ -157,7 +157,7 @@ id: FaceEyebrowLeft bodyPart: Face markingCategory: Face - speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne] coloring: default: type: @@ -172,7 +172,7 @@ id: FaceEyebrows bodyPart: Face markingCategory: Face - speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne] coloring: default: type: @@ -186,7 +186,7 @@ id: FaceEyecornerRight bodyPart: Face markingCategory: Face - speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne] coloring: default: type: @@ -200,7 +200,7 @@ id: FaceEyecornerLeft bodyPart: Face markingCategory: Face - speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne] coloring: default: type: @@ -214,7 +214,7 @@ id: FaceEyelashRight bodyPart: Face markingCategory: Face - speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne] coloring: default: type: @@ -228,7 +228,7 @@ id: FaceEyelashLeft bodyPart: Face markingCategory: Face - speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne] coloring: default: type: @@ -242,7 +242,7 @@ id: FaceEyestripe bodyPart: Face markingCategory: Face - speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Arachne] coloring: default: type: @@ -256,7 +256,7 @@ id: FaceLipcornerRight bodyPart: Face markingCategory: Face - speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne] coloring: default: type: @@ -270,7 +270,7 @@ id: FaceLipcornerLeft bodyPart: Face markingCategory: Face - speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne] coloring: default: type: @@ -284,7 +284,7 @@ id: FaceGlabella bodyPart: Face markingCategory: Face - speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne] coloring: default: type: @@ -298,7 +298,7 @@ id: FaceLowercheekRight bodyPart: Face markingCategory: Face - speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne] coloring: default: type: @@ -312,7 +312,7 @@ id: FaceLowercheekLeft bodyPart: Face markingCategory: Face - speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne] coloring: default: type: @@ -326,7 +326,7 @@ id: FaceNosetape bodyPart: Face markingCategory: Face - speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Arachne] coloring: default: type: @@ -340,7 +340,7 @@ id: FaceNosetip bodyPart: Face markingCategory: Face - speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Arachne] coloring: default: type: @@ -355,7 +355,7 @@ id: FaceNosestripe bodyPart: Face markingCategory: Face - speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Arachne] coloring: default: type: @@ -369,7 +369,7 @@ id: FaceUnibrow bodyPart: Face markingCategory: Face - speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne] coloring: default: type: @@ -383,7 +383,7 @@ id: FaceNeckSlim bodyPart: Face markingCategory: Face - speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne] followSkinColor: true sprites: - sprite: Mobs/Customization/face.rsi @@ -393,7 +393,7 @@ id: FaceNeckWide bodyPart: Face markingCategory: Face - speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne] followSkinColor: true sprites: - sprite: Mobs/Customization/face.rsi @@ -403,7 +403,7 @@ id: FaceNeckSlimThick bodyPart: Face markingCategory: Face - speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne] followSkinColor: true sprites: - sprite: Mobs/Customization/face.rsi @@ -413,7 +413,7 @@ id: FaceNeckWideThick bodyPart: Face markingCategory: Face - speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne, Lamia] + speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Reptilian, Vulpkanin, Arachne] followSkinColor: true sprites: - sprite: Mobs/Customization/face.rsi diff --git a/Resources/Prototypes/Entities/Mobs/Customization/Markings/gauze.yml b/Resources/Prototypes/Entities/Mobs/Customization/Markings/gauze.yml index c304e9c531..6e8f7dc819 100644 --- a/Resources/Prototypes/Entities/Mobs/Customization/Markings/gauze.yml +++ b/Resources/Prototypes/Entities/Mobs/Customization/Markings/gauze.yml @@ -2,7 +2,7 @@ id: GauzeLefteyePatch bodyPart: Eyes markingCategory: Overlay - speciesRestriction: [Dwarf, Human, Arachnid, Felinid, Oni, Vulpkanin, Arachne, Lamia] # Delta V - Felinid, Oni, Vulpkanin + speciesRestriction: [Dwarf, Human, Arachnid, Felinid, Oni, Vulpkanin, Arachne] # Delta V - Felinid, Oni, Vulpkanin coloring: default: type: @@ -16,7 +16,7 @@ id: GauzeLefteyePad bodyPart: Eyes markingCategory: Overlay - speciesRestriction: [Dwarf, Human, Reptilian, Arachnid, Felinid, Oni, Vulpkanin, Arachne, Lamia] # Delta V - Felinid, Oni, Vulpkanin + speciesRestriction: [Dwarf, Human, Reptilian, Arachnid, Felinid, Oni, Vulpkanin, Arachne] # Delta V - Felinid, Oni, Vulpkanin coloring: default: type: @@ -30,7 +30,7 @@ id: GauzeRighteyePatch bodyPart: Eyes markingCategory: Overlay - speciesRestriction: [Dwarf, Human, Arachnid, Felinid, Oni, Vulpkanin, Arachne, Lamia] # Delta V - Felinid, Oni, Vulpkanin + speciesRestriction: [Dwarf, Human, Arachnid, Felinid, Oni, Vulpkanin, Arachne] # Delta V - Felinid, Oni, Vulpkanin coloring: default: type: @@ -44,7 +44,7 @@ id: GauzeRighteyePad bodyPart: Eyes markingCategory: Overlay - speciesRestriction: [Dwarf, Human, Reptilian, Arachnid, Felinid, Oni, Vulpkanin, Arachne, Lamia] # Delta V - Felinid, Oni, Vulpkanin + speciesRestriction: [Dwarf, Human, Reptilian, Arachnid, Felinid, Oni, Vulpkanin, Arachne] # Delta V - Felinid, Oni, Vulpkanin coloring: default: type: @@ -58,7 +58,7 @@ id: GauzeBlindfold bodyPart: Eyes markingCategory: Overlay - speciesRestriction: [Dwarf, Human, Arachnid, Felinid, Oni, Harpy, Vulpkanin, Arachne, Lamia] # Delta V - Felinid, Oni, Harpy, Vulpkanin + speciesRestriction: [Dwarf, Human, Arachnid, Felinid, Oni, Harpy, Vulpkanin, Arachne] # Delta V - Felinid, Oni, Harpy, Vulpkanin coloring: default: type: @@ -72,7 +72,7 @@ id: GauzeShoulder bodyPart: Chest markingCategory: Overlay - speciesRestriction: [Dwarf, Human, Reptilian, Arachnid, Felinid, Oni, Vulpkanin, Arachne, Lamia] # Delta V - Felinid, Oni, Vulpkanin + speciesRestriction: [Dwarf, Human, Reptilian, Arachnid, Felinid, Oni, Vulpkanin, Arachne] # Delta V - Felinid, Oni, Vulpkanin coloring: default: type: @@ -86,7 +86,7 @@ id: GauzeStomach bodyPart: Chest markingCategory: Overlay - speciesRestriction: [Dwarf, Human, Reptilian, Arachnid, Felinid, Oni, Vulpkanin, Arachne, Lamia] # Delta V - Felinid, Oni, Vulpkanin + speciesRestriction: [Dwarf, Human, Reptilian, Arachnid, Felinid, Oni, Vulpkanin, Arachne] # Delta V - Felinid, Oni, Vulpkanin coloring: default: type: @@ -100,7 +100,7 @@ id: GauzeUpperArmRight bodyPart: RArm markingCategory: Overlay - speciesRestriction: [Dwarf, Human, Reptilian, Arachnid, SlimePerson, Felinid, Oni, Vulpkanin, Arachne, Lamia] # Delta V - Felinid, Oni, Vulpkanin + speciesRestriction: [Dwarf, Human, Reptilian, Arachnid, SlimePerson, Felinid, Oni, Vulpkanin, Arachne] # Delta V - Felinid, Oni, Vulpkanin coloring: default: type: @@ -114,7 +114,7 @@ id: GauzeLowerArmRight bodyPart: RArm, RHand markingCategory: Overlay - speciesRestriction: [Dwarf, Human, Reptilian, Arachnid, SlimePerson, Felinid, Oni, Vulpkanin, Arachne, Lamia] # Delta V - Felinid, Oni, Vulpkanin + speciesRestriction: [Dwarf, Human, Reptilian, Arachnid, SlimePerson, Felinid, Oni, Vulpkanin, Arachne] # Delta V - Felinid, Oni, Vulpkanin coloring: default: type: @@ -198,7 +198,7 @@ id: GauzeBoxerWrapRight bodyPart: RHand markingCategory: Overlay - speciesRestriction: [Dwarf, Human, Reptilian, Arachnid, SlimePerson, Felinid, Oni, Vulpkanin, Arachne, Lamia] # Delta V - Felinid, Oni, Vulpkanin + speciesRestriction: [Dwarf, Human, Reptilian, Arachnid, SlimePerson, Felinid, Oni, Vulpkanin, Arachne] # Delta V - Felinid, Oni, Vulpkanin coloring: default: type: @@ -212,7 +212,7 @@ id: GauzeBoxerWrapLeft bodyPart: LHand markingCategory: Overlay - speciesRestriction: [Dwarf, Human, Reptilian, Arachnid, SlimePerson, Felinid, Oni, Vulpkanin, Arachne, Lamia] # Delta V - Felinid, Oni, Vulpkanin + speciesRestriction: [Dwarf, Human, Reptilian, Arachnid, SlimePerson, Felinid, Oni, Vulpkanin, Arachne] # Delta V - Felinid, Oni, Vulpkanin coloring: default: type: diff --git a/Resources/Prototypes/Entities/Mobs/Customization/Markings/human_noses.yml b/Resources/Prototypes/Entities/Mobs/Customization/Markings/human_noses.yml index 1319e4f539..a6ea6a6a42 100644 --- a/Resources/Prototypes/Entities/Mobs/Customization/Markings/human_noses.yml +++ b/Resources/Prototypes/Entities/Mobs/Customization/Markings/human_noses.yml @@ -4,7 +4,7 @@ markingCategory: Snout followSkinColor: true forcedColoring: true - speciesRestriction: [Human, Dwarf, Felinid, Oni, Harpy, Arachne, Lamia] + speciesRestriction: [Human, Dwarf, Felinid, Oni, Harpy, Arachne] sprites: - sprite: Mobs/Customization/human_noses.rsi state: schnozz @@ -15,7 +15,7 @@ markingCategory: Snout followSkinColor: true forcedColoring: true - speciesRestriction: [Human, Dwarf, Felinid, Oni, Harpy, Arachne, Lamia] + speciesRestriction: [Human, Dwarf, Felinid, Oni, Harpy, Arachne] sprites: - sprite: Mobs/Customization/human_noses.rsi state: nubby @@ -26,7 +26,7 @@ markingCategory: Snout followSkinColor: true forcedColoring: true - speciesRestriction: [Human, Dwarf, Felinid, Oni, Harpy, Arachne, Lamia] + speciesRestriction: [Human, Dwarf, Felinid, Oni, Harpy, Arachne] sprites: - sprite: Mobs/Customization/human_noses.rsi state: droop @@ -37,7 +37,7 @@ markingCategory: Snout followSkinColor: true forcedColoring: true - speciesRestriction: [Human, Dwarf, Felinid, Oni, Harpy, Arachne, Lamia] + speciesRestriction: [Human, Dwarf, Felinid, Oni, Harpy, Arachne] sprites: - sprite: Mobs/Customization/human_noses.rsi state: blob @@ -48,7 +48,7 @@ markingCategory: Snout followSkinColor: true forcedColoring: true - speciesRestriction: [Human, Dwarf, Felinid, Oni, Harpy, Arachne, Lamia] + speciesRestriction: [Human, Dwarf, Felinid, Oni, Harpy, Arachne] sprites: - sprite: Mobs/Customization/human_noses.rsi state: uppie diff --git a/Resources/Prototypes/Entities/Mobs/Customization/Markings/makeup.yml b/Resources/Prototypes/Entities/Mobs/Customization/Markings/makeup.yml index ec954a5655..9ec2022b05 100644 --- a/Resources/Prototypes/Entities/Mobs/Customization/Markings/makeup.yml +++ b/Resources/Prototypes/Entities/Mobs/Customization/Markings/makeup.yml @@ -2,7 +2,7 @@ id: MakeupLips bodyPart: Face markingCategory: Face - speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Arachne, Lamia] # Delta V - Felinid, Oni, Harpy + speciesRestriction: [Dwarf, Human, SlimePerson, Felinid, Oni, Harpy, Arachne] # Delta V - Felinid, Oni, Harpy coloring: default: type: @@ -16,7 +16,7 @@ id: MakeupBlush bodyPart: Face markingCategory: Face - speciesRestriction: [Dwarf, Human, Reptilian, SlimePerson, Felinid, Oni, Vulpkanin, Harpy, Arachne, Lamia] # Delta V - Felinid, Oni, Vulpkanin, Harpy + speciesRestriction: [Dwarf, Human, Reptilian, SlimePerson, Felinid, Oni, Vulpkanin, Harpy, Arachne] # Delta V - Felinid, Oni, Vulpkanin, Harpy coloring: default: type: @@ -30,7 +30,7 @@ id: MakeupNailPolishRight bodyPart: RHand markingCategory: RightHand - speciesRestriction: [Dwarf, Human, Reptilian, Arachnid, SlimePerson, Felinid, Oni, Vulpkanin, Arachne, Lamia] # Delta V - Felinid, Oni, Vulpkanin + speciesRestriction: [Dwarf, Human, Reptilian, Arachnid, SlimePerson, Felinid, Oni, Vulpkanin, Arachne] # Delta V - Felinid, Oni, Vulpkanin coloring: default: type: @@ -44,7 +44,7 @@ id: MakeupNailPolishLeft bodyPart: LHand markingCategory: LeftHand - speciesRestriction: [Dwarf, Human, Reptilian, Arachnid, SlimePerson, Felinid, Oni, Vulpkanin, Arachne, Lamia] # Delta V - Felinid, Oni, Vulpkanin + speciesRestriction: [Dwarf, Human, Reptilian, Arachnid, SlimePerson, Felinid, Oni, Vulpkanin, Arachne] # Delta V - Felinid, Oni, Vulpkanin coloring: default: type: @@ -73,7 +73,7 @@ - type: marking id: MakeupMothBlush bodyPart: Face - markingCategory: Face + markingCategory: Overlay speciesRestriction: [Moth] coloring: default: diff --git a/Resources/Prototypes/Entities/Mobs/Customization/Markings/pointy_ears.yml b/Resources/Prototypes/Entities/Mobs/Customization/Markings/pointy_ears.yml index 53a0beac3f..237275e419 100644 --- a/Resources/Prototypes/Entities/Mobs/Customization/Markings/pointy_ears.yml +++ b/Resources/Prototypes/Entities/Mobs/Customization/Markings/pointy_ears.yml @@ -3,7 +3,7 @@ bodyPart: HeadSide markingCategory: HeadSide forcedColoring: true - speciesRestriction: [Oni, Harpy, Arachne, Lamia] + speciesRestriction: [Oni, Harpy, Arachne] sprites: - sprite: Mobs/Customization/pointy_ears.rsi state: pointy_ears_standard @@ -13,7 +13,7 @@ bodyPart: HeadSide markingCategory: HeadSide forcedColoring: true - speciesRestriction: [Oni, Harpy, Arachne, Lamia] + speciesRestriction: [Oni, Harpy, Arachne] sprites: - sprite: Mobs/Customization/pointy_ears.rsi state: pointy_ears_wide @@ -23,7 +23,7 @@ bodyPart: HeadSide markingCategory: HeadSide forcedColoring: true - speciesRestriction: [Oni, Harpy, Arachne, Lamia] + speciesRestriction: [Oni, Harpy, Arachne] sprites: - sprite: Mobs/Customization/pointy_ears.rsi state: pointy_ears_small @@ -33,7 +33,7 @@ bodyPart: HeadSide markingCategory: HeadSide forcedColoring: true - speciesRestriction: [Oni, Harpy, Arachne, Lamia] + speciesRestriction: [Oni, Harpy, Arachne] sprites: - sprite: Mobs/Customization/pointy_ears.rsi state: pointy_ears_upwards @@ -43,7 +43,7 @@ bodyPart: HeadSide markingCategory: HeadSide forcedColoring: true - speciesRestriction: [Oni, Harpy, Arachne, Lamia] + speciesRestriction: [Oni, Harpy, Arachne] sprites: - sprite: Mobs/Customization/pointy_ears.rsi state: pointy_ears_tall @@ -53,7 +53,7 @@ bodyPart: HeadSide markingCategory: HeadSide forcedColoring: true - speciesRestriction: [Oni, Harpy, Arachne, Lamia] + speciesRestriction: [Oni, Harpy, Arachne] sprites: - sprite: Mobs/Customization/pointy_ears.rsi state: pointy_ears_slanted @@ -63,7 +63,7 @@ bodyPart: HeadSide markingCategory: HeadSide forcedColoring: true - speciesRestriction: [Oni, Harpy, Arachne, Lamia] + speciesRestriction: [Oni, Harpy, Arachne] sprites: - sprite: Mobs/Customization/pointy_ears.rsi state: pointy_ears_thin @@ -73,7 +73,7 @@ bodyPart: HeadSide markingCategory: HeadSide forcedColoring: true - speciesRestriction: [Oni, Harpy, Arachne, Lamia] + speciesRestriction: [Oni, Harpy, Arachne] sprites: - sprite: Mobs/Customization/pointy_ears.rsi state: pointy_ears_large @@ -83,7 +83,7 @@ bodyPart: HeadSide markingCategory: HeadSide forcedColoring: true - speciesRestriction: [Oni, Harpy, Arachne, Lamia] + speciesRestriction: [Oni, Harpy, Arachne] sprites: - sprite: Mobs/Customization/pointy_ears.rsi state: pointy_ears_none diff --git a/Resources/Prototypes/Entities/Mobs/Customization/Markings/reptilian.yml b/Resources/Prototypes/Entities/Mobs/Customization/Markings/reptilian.yml index 0ba3ecc695..7be52c11f0 100644 --- a/Resources/Prototypes/Entities/Mobs/Customization/Markings/reptilian.yml +++ b/Resources/Prototypes/Entities/Mobs/Customization/Markings/reptilian.yml @@ -67,7 +67,7 @@ id: LizardFrillsNeckfull bodyPart: HeadSide markingCategory: HeadSide - speciesRestriction: [Reptilian, Lamia] + speciesRestriction: [Reptilian] sprites: - sprite: Mobs/Customization/reptilian_parts.rsi state: frills_neckfull @@ -76,7 +76,7 @@ id: LizardHornsAngler bodyPart: HeadTop markingCategory: HeadTop - speciesRestriction: [Reptilian, Human, Lamia, Kobold] # Shitmed Change + speciesRestriction: [Reptilian, Human, Kobold] # Shitmed Change sprites: - sprite: Mobs/Customization/reptilian_parts.rsi state: horns_angler @@ -85,7 +85,7 @@ id: LizardHornsCurled bodyPart: HeadTop markingCategory: HeadTop - speciesRestriction: [Reptilian, Human, Lamia, Kobold] # Shitmed Change + speciesRestriction: [Reptilian, Human, Kobold] # Shitmed Change sprites: - sprite: Mobs/Customization/reptilian_parts.rsi state: horns_curled @@ -94,7 +94,7 @@ id: LizardHornsRam bodyPart: HeadTop markingCategory: HeadTop - speciesRestriction: [Reptilian, Human, Lamia, Kobold] # Shitmed Change + speciesRestriction: [Reptilian, Human, Kobold] # Shitmed Change sprites: - sprite: Mobs/Customization/reptilian_parts.rsi state: horns_ram @@ -103,7 +103,7 @@ id: LizardHornsShort bodyPart: HeadTop markingCategory: HeadTop - speciesRestriction: [Reptilian, Human, Lamia, Kobold] # Shitmed Change + speciesRestriction: [Reptilian, Human, Kobold] # Shitmed Change sprites: - sprite: Mobs/Customization/reptilian_parts.rsi state: horns_short @@ -112,7 +112,7 @@ id: LizardHornsSimple bodyPart: HeadTop markingCategory: HeadTop - speciesRestriction: [Reptilian, Human, Lamia, Kobold] # Shitmed Change + speciesRestriction: [Reptilian, Human, Kobold] # Shitmed Change sprites: - sprite: Mobs/Customization/reptilian_parts.rsi state: horns_simple @@ -121,7 +121,7 @@ id: LizardHornsDouble bodyPart: HeadTop markingCategory: HeadTop - speciesRestriction: [Reptilian, Human, Lamia, Kobold] # Shitmed Change + speciesRestriction: [Reptilian, Human, Kobold] # Shitmed Change sprites: - sprite: Mobs/Customization/reptilian_parts.rsi state: horns_double @@ -208,7 +208,7 @@ id: LizardChestTiger bodyPart: Chest markingCategory: Chest - speciesRestriction: [Reptilian, Shadowkin, Lamia] + speciesRestriction: [Reptilian, Shadowkin] sprites: - sprite: Mobs/Customization/reptilian_parts.rsi state: body_tiger @@ -226,7 +226,7 @@ id: LizardLArmTiger bodyPart: LArm markingCategory: LeftArm - speciesRestriction: [Reptilian, Lamia] + speciesRestriction: [Reptilian] sprites: - sprite: Mobs/Customization/reptilian_parts.rsi state: l_arm_tiger @@ -244,7 +244,7 @@ id: LizardRArmTiger bodyPart: RArm markingCategory: RightArm - speciesRestriction: [Reptilian, Lamia] + speciesRestriction: [Reptilian] sprites: - sprite: Mobs/Customization/reptilian_parts.rsi state: r_arm_tiger @@ -262,7 +262,7 @@ id: LizardHornsArgali bodyPart: HeadTop markingCategory: HeadTop - speciesRestriction: [Reptilian, Human, Lamia, Kobold] # Shitmed Change + speciesRestriction: [Reptilian, Human, Kobold] # Shitmed Change sprites: - sprite: Mobs/Customization/reptilian_parts.rsi state: horns_argali @@ -271,7 +271,7 @@ id: LizardHornsAyrshire bodyPart: HeadTop markingCategory: HeadTop - speciesRestriction: [Reptilian, Human, Lamia, Kobold] # Shitmed Change + speciesRestriction: [Reptilian, Human, Kobold] # Shitmed Change sprites: - sprite: Mobs/Customization/reptilian_parts.rsi state: horns_ayrshire @@ -280,7 +280,7 @@ id: LizardHornsMyrsore bodyPart: HeadTop markingCategory: HeadTop - speciesRestriction: [Reptilian, Human, Lamia, Kobold] # Shitmed Change + speciesRestriction: [Reptilian, Human, Kobold] # Shitmed Change sprites: - sprite: Mobs/Customization/reptilian_parts.rsi state: horns_myrsore @@ -289,7 +289,7 @@ id: LizardHornsBighorn bodyPart: HeadTop markingCategory: HeadTop - speciesRestriction: [Reptilian, Human, Lamia, Kobold] # Shitmed Change + speciesRestriction: [Reptilian, Human, Kobold] # Shitmed Change sprites: - sprite: Mobs/Customization/reptilian_parts.rsi state: horns_bighorn @@ -325,7 +325,7 @@ id: LizardChestBackspikes bodyPart: Chest markingCategory: Chest - speciesRestriction: [Reptilian, Lamia] + speciesRestriction: [Reptilian] sprites: - sprite: Mobs/Customization/reptilian_parts.rsi state: body_backspikes diff --git a/Resources/Prototypes/Entities/Mobs/Customization/Markings/scars.yml b/Resources/Prototypes/Entities/Mobs/Customization/Markings/scars.yml index 150c6278d0..c9050975aa 100644 --- a/Resources/Prototypes/Entities/Mobs/Customization/Markings/scars.yml +++ b/Resources/Prototypes/Entities/Mobs/Customization/Markings/scars.yml @@ -2,7 +2,7 @@ id: ScarEyeRight bodyPart: Head markingCategory: Head - speciesRestriction: [Human, Dwarf, Felinid, Harpy, Oni, Arachne, Lamia] # Delta V - Felinid, Oni, Harpy + speciesRestriction: [Human, Dwarf, Felinid, Harpy, Oni, Arachne] # Delta V - Felinid, Oni, Harpy followSkinColor: true sprites: - sprite: Mobs/Customization/scars.rsi @@ -12,7 +12,7 @@ id: ScarEyeLeft bodyPart: Head markingCategory: Head - speciesRestriction: [Human, Dwarf, Felinid, Harpy, Oni, Arachne, Lamia] # Delta V - Felinid, Oni, Harpy + speciesRestriction: [Human, Dwarf, Felinid, Harpy, Oni, Arachne] # Delta V - Felinid, Oni, Harpy followSkinColor: true sprites: - sprite: Mobs/Customization/scars.rsi @@ -22,7 +22,7 @@ id: ScarTopSurgeryShort bodyPart: Chest markingCategory: Chest - speciesRestriction: [Human, Dwarf, Felinid, Oni, Arachne, Lamia] + speciesRestriction: [Human, Dwarf, Felinid, Oni, Arachne] sexRestriction: [Male] followSkinColor: true sprites: @@ -33,7 +33,7 @@ id: ScarTopSurgeryLong bodyPart: Chest markingCategory: Chest - speciesRestriction: [Human, Dwarf, Felinid, Oni, Arachne, Lamia] + speciesRestriction: [Human, Dwarf, Felinid, Oni, Arachne] sexRestriction: [Male] followSkinColor: true sprites: @@ -44,7 +44,7 @@ id: ScarChest bodyPart: Chest markingCategory: Chest - speciesRestriction: [Human, Dwarf, Felinid, Oni, Arachne, Lamia] + speciesRestriction: [Human, Dwarf, Felinid, Oni, Arachne] followSkinColor: true sprites: - sprite: Mobs/Customization/scars.rsi diff --git a/Resources/Prototypes/Entities/Mobs/Customization/Markings/tattoos.yml b/Resources/Prototypes/Entities/Mobs/Customization/Markings/tattoos.yml index 5aaf5d4870..8b65988566 100644 --- a/Resources/Prototypes/Entities/Mobs/Customization/Markings/tattoos.yml +++ b/Resources/Prototypes/Entities/Mobs/Customization/Markings/tattoos.yml @@ -2,7 +2,7 @@ id: TattooHiveChest bodyPart: Chest markingCategory: Chest - speciesRestriction: [Human, Dwarf, Felinid, Oni, Shadowkin, Arachne, Lamia] # Delta V - Felinid, Oni + speciesRestriction: [Human, Dwarf, Felinid, Oni, Shadowkin, Arachne] # Delta V - Felinid, Oni coloring: default: type: @@ -16,7 +16,7 @@ id: TattooNightlingChest bodyPart: Chest markingCategory: Chest - speciesRestriction: [Human, Dwarf, Felinid, Oni, Shadowkin, Arachne, Lamia] # Delta V - Felinid, Oni + speciesRestriction: [Human, Dwarf, Felinid, Oni, Shadowkin, Arachne] # Delta V - Felinid, Oni coloring: default: type: @@ -58,7 +58,7 @@ id: TattooCampbellLeftArm bodyPart: LArm markingCategory: LeftArm - speciesRestriction: [Human, Dwarf, Felinid, Oni, Shadowkin, Arachne, Lamia] # Delta V - Felinid, Oni + speciesRestriction: [Human, Dwarf, Felinid, Oni, Shadowkin, Arachne] # Delta V - Felinid, Oni coloring: default: type: @@ -72,7 +72,7 @@ id: TattooCampbellRightArm bodyPart: RArm markingCategory: RightArm - speciesRestriction: [Human, Dwarf, Felinid, Oni, Shadowkin, Arachne, Lamia] # Delta V - Felinid, Oni + speciesRestriction: [Human, Dwarf, Felinid, Oni, Shadowkin, Arachne] # Delta V - Felinid, Oni coloring: default: type: @@ -114,7 +114,7 @@ id: TattooEyeRight bodyPart: Eyes markingCategory: Head - speciesRestriction: [Human, SlimePerson, Reptilian, Dwarf, Felinid, Oni, Harpy, Lamia] # Delta V - Felinid, Oni, Harpy + speciesRestriction: [Human, SlimePerson, Reptilian, Dwarf, Felinid, Oni, Harpy] # Delta V - Felinid, Oni, Harpy coloring: default: type: @@ -128,7 +128,7 @@ id: TattooEyeLeft bodyPart: Eyes markingCategory: Head - speciesRestriction: [Human, SlimePerson, Reptilian, Dwarf, Felinid, Oni, Harpy, Lamia] # Delta V - Felinid, Oni, Harpy + speciesRestriction: [Human, SlimePerson, Reptilian, Dwarf, Felinid, Oni, Harpy] # Delta V - Felinid, Oni, Harpy coloring: default: type: diff --git a/Resources/Prototypes/Entities/Mobs/Customization/Markings/wrist.yml b/Resources/Prototypes/Entities/Mobs/Customization/Markings/wrist.yml index 01bd1cf85a..0e2f9be912 100644 --- a/Resources/Prototypes/Entities/Mobs/Customization/Markings/wrist.yml +++ b/Resources/Prototypes/Entities/Mobs/Customization/Markings/wrist.yml @@ -2,7 +2,7 @@ id: WristBraceletRight bodyPart: RHand markingCategory: RightHand - speciesRestriction: [Human, Dwarf, SlimePerson, Arachnid, Reptilian, Diona, Felinid, Oni, Vulpkanin, Gingerbread, Arachne, Lamia] + speciesRestriction: [Human, Dwarf, SlimePerson, Arachnid, Reptilian, Diona, Felinid, Oni, Vulpkanin, Gingerbread, Arachne] coloring: default: type: @@ -17,7 +17,7 @@ id: WristBraceletLeft bodyPart: LHand markingCategory: LeftHand - speciesRestriction: [Human, Dwarf, SlimePerson, Arachnid, Reptilian, Felinid, Oni, Vulpkanin, Gingerbread, Arachne, Lamia] + speciesRestriction: [Human, Dwarf, SlimePerson, Arachnid, Reptilian, Felinid, Oni, Vulpkanin, Gingerbread, Arachne] coloring: default: type: @@ -31,7 +31,7 @@ id: WristBraceletArmRight bodyPart: RArm markingCategory: RightArm - speciesRestriction: [Human, Dwarf, SlimePerson, Arachnid, Reptilian, Diona, Felinid, Oni, Vulpkanin, Gingerbread, Arachne, Lamia] + speciesRestriction: [Human, Dwarf, SlimePerson, Arachnid, Reptilian, Diona, Felinid, Oni, Vulpkanin, Gingerbread, Arachne] coloring: default: type: @@ -45,7 +45,7 @@ id: WristBraceletArmLeft bodyPart: LArm markingCategory: LeftArm - speciesRestriction: [Human, Dwarf, SlimePerson, Arachnid, Reptilian, Felinid, Oni, Vulpkanin, Gingerbread, Arachne, Lamia] + speciesRestriction: [Human, Dwarf, SlimePerson, Arachnid, Reptilian, Felinid, Oni, Vulpkanin, Gingerbread, Arachne] coloring: default: type: @@ -59,7 +59,7 @@ id: WristWatchRight bodyPart: RHand markingCategory: RightHand - speciesRestriction: [Human, Dwarf, SlimePerson, Arachnid, Reptilian, Diona, Felinid, Oni, Vulpkanin, Gingerbread, Arachne, Lamia] + speciesRestriction: [Human, Dwarf, SlimePerson, Arachnid, Reptilian, Diona, Felinid, Oni, Vulpkanin, Gingerbread, Arachne] coloring: default: type: @@ -73,7 +73,7 @@ id: WristWatchLeft bodyPart: LHand markingCategory: LeftHand - speciesRestriction: [Human, Dwarf, SlimePerson, Arachnid, Reptilian, Felinid, Oni, Vulpkanin, Gingerbread, Arachne, Lamia] + speciesRestriction: [Human, Dwarf, SlimePerson, Arachnid, Reptilian, Felinid, Oni, Vulpkanin, Gingerbread, Arachne] coloring: default: type: @@ -87,7 +87,7 @@ id: WristWatchSilverRight bodyPart: RHand markingCategory: RightHand - speciesRestriction: [Human, Dwarf, SlimePerson, Arachnid, Reptilian, Diona, Felinid, Oni, Vulpkanin, Gingerbread, Arachne, Lamia] + speciesRestriction: [Human, Dwarf, SlimePerson, Arachnid, Reptilian, Diona, Felinid, Oni, Vulpkanin, Gingerbread, Arachne] coloring: default: type: @@ -101,7 +101,7 @@ id: WristWatchSilverLeft bodyPart: LHand markingCategory: LeftHand - speciesRestriction: [Human, Dwarf, SlimePerson, Arachnid, Reptilian, Felinid, Oni, Vulpkanin, Gingerbread, Arachne, Lamia] + speciesRestriction: [Human, Dwarf, SlimePerson, Arachnid, Reptilian, Felinid, Oni, Vulpkanin, Gingerbread, Arachne] coloring: default: type: @@ -115,7 +115,7 @@ id: WristWatchGoldRight bodyPart: RHand markingCategory: RightHand - speciesRestriction: [Human, Dwarf, SlimePerson, Arachnid, Reptilian, Diona, Felinid, Oni, Vulpkanin, Gingerbread, Arachne, Lamia] + speciesRestriction: [Human, Dwarf, SlimePerson, Arachnid, Reptilian, Diona, Felinid, Oni, Vulpkanin, Gingerbread, Arachne] coloring: default: type: @@ -129,7 +129,7 @@ id: WristWatchGoldLeft bodyPart: LHand markingCategory: LeftHand - speciesRestriction: [Human, Dwarf, SlimePerson, Arachnid, Reptilian, Felinid, Oni, Vulpkanin, Gingerbread, Arachne, Lamia] + speciesRestriction: [Human, Dwarf, SlimePerson, Arachnid, Reptilian, Felinid, Oni, Vulpkanin, Gingerbread, Arachne] coloring: default: type: @@ -143,7 +143,7 @@ id: WristWatchHoloRight bodyPart: RHand markingCategory: RightHand - speciesRestriction: [Human, Dwarf, SlimePerson, Arachnid, Reptilian, Diona, Felinid, Oni, Vulpkanin, Gingerbread, Arachne, Lamia] + speciesRestriction: [Human, Dwarf, SlimePerson, Arachnid, Reptilian, Diona, Felinid, Oni, Vulpkanin, Gingerbread, Arachne] coloring: default: type: @@ -157,7 +157,7 @@ id: WristWatchHoloLeft bodyPart: LHand markingCategory: LeftHand - speciesRestriction: [Human, Dwarf, SlimePerson, Arachnid, Reptilian, Felinid, Oni, Vulpkanin, Gingerbread, Arachne, Lamia] + speciesRestriction: [Human, Dwarf, SlimePerson, Arachnid, Reptilian, Felinid, Oni, Vulpkanin, Gingerbread, Arachne] coloring: default: type: @@ -171,7 +171,7 @@ id: WristWatchLeatherRight bodyPart: RHand markingCategory: RightHand - speciesRestriction: [Human, Dwarf, SlimePerson, Arachnid, Reptilian, Diona, Felinid, Oni, Vulpkanin, Gingerbread, Arachne, Lamia] + speciesRestriction: [Human, Dwarf, SlimePerson, Arachnid, Reptilian, Diona, Felinid, Oni, Vulpkanin, Gingerbread, Arachne] coloring: default: type: @@ -185,7 +185,7 @@ id: WristWatchLeatherLeft bodyPart: LHand markingCategory: LeftHand - speciesRestriction: [Human, Dwarf, SlimePerson, Arachnid, Reptilian, Felinid, Oni, Vulpkanin, Gingerbread, Arachne, Lamia] + speciesRestriction: [Human, Dwarf, SlimePerson, Arachnid, Reptilian, Felinid, Oni, Vulpkanin, Gingerbread, Arachne] coloring: default: type: @@ -199,7 +199,7 @@ id: WristWatchColorableRight bodyPart: RHand markingCategory: RightHand - speciesRestriction: [Human, Dwarf, SlimePerson, Arachnid, Reptilian, Diona, Felinid, Oni, Vulpkanin, Gingerbread, Arachne, Lamia] + speciesRestriction: [Human, Dwarf, SlimePerson, Arachnid, Reptilian, Diona, Felinid, Oni, Vulpkanin, Gingerbread, Arachne] coloring: default: type: @@ -215,7 +215,7 @@ id: WristWatchColorableLeft bodyPart: LHand markingCategory: LeftHand - speciesRestriction: [Human, Dwarf, SlimePerson, Arachnid, Reptilian, Felinid, Oni, Vulpkanin, Gingerbread, Arachne, Lamia] + speciesRestriction: [Human, Dwarf, SlimePerson, Arachnid, Reptilian, Felinid, Oni, Vulpkanin, Gingerbread, Arachne] coloring: default: type: diff --git a/Resources/Prototypes/Entities/Mobs/Customization/cyberlimbs/bishop.yml b/Resources/Prototypes/Entities/Mobs/Customization/cyberlimbs/bishop.yml index b491d9b918..2ae4bc6d62 100644 --- a/Resources/Prototypes/Entities/Mobs/Customization/cyberlimbs/bishop.yml +++ b/Resources/Prototypes/Entities/Mobs/Customization/cyberlimbs/bishop.yml @@ -42,7 +42,7 @@ id: CyberLimbsMarkingBishopLArm bodyPart: LArm markingCategory: LeftArm - speciesRestriction: [IPC, Moth, Dwarf, Human, Arachnid, Felinid, Oni, Vulpkanin, HumanoidFoxes, Reptilian, Arachne, Lamia] + speciesRestriction: [IPC, Moth, Dwarf, Human, Arachnid, Felinid, Oni, Vulpkanin, HumanoidFoxes, Reptilian, Arachne] sprites: - sprite: Mobs/Customization/cyberlimbs/bishop/bishop_main.rsi state: l_arm-primary @@ -55,7 +55,7 @@ id: CyberLimbsMarkingBishopLHand bodyPart: LHand markingCategory: LeftHand - speciesRestriction: [IPC, Moth, Dwarf, Human, Arachnid, Felinid, Oni, Vulpkanin, HumanoidFoxes, Reptilian, Arachne, Lamia] + speciesRestriction: [IPC, Moth, Dwarf, Human, Arachnid, Felinid, Oni, Vulpkanin, HumanoidFoxes, Reptilian, Arachne] sprites: - sprite: Mobs/Customization/cyberlimbs/bishop/bishop_main.rsi state: l_hand @@ -87,7 +87,7 @@ id: CyberLimbsMarkingBishopRArm bodyPart: RArm markingCategory: RightArm - speciesRestriction: [IPC, Moth, Dwarf, Human, Arachnid, Felinid, Oni, Vulpkanin, HumanoidFoxes, Reptilian, Arachne, Lamia] + speciesRestriction: [IPC, Moth, Dwarf, Human, Arachnid, Felinid, Oni, Vulpkanin, HumanoidFoxes, Reptilian, Arachne] sprites: - sprite: Mobs/Customization/cyberlimbs/bishop/bishop_main.rsi state: r_arm-primary @@ -101,7 +101,7 @@ id: CyberLimbsMarkingBishopRHand bodyPart: RHand markingCategory: RightHand - speciesRestriction: [IPC, Moth, Dwarf, Human, Arachnid, Felinid, Oni, Vulpkanin, HumanoidFoxes, Reptilian, Arachne, Lamia] + speciesRestriction: [IPC, Moth, Dwarf, Human, Arachnid, Felinid, Oni, Vulpkanin, HumanoidFoxes, Reptilian, Arachne] sprites: - sprite: Mobs/Customization/cyberlimbs/bishop/bishop_main.rsi state: r_hand diff --git a/Resources/Prototypes/Entities/Mobs/Customization/cyberlimbs/hesphiastos.yml b/Resources/Prototypes/Entities/Mobs/Customization/cyberlimbs/hesphiastos.yml index e907975116..d6e2194bf3 100644 --- a/Resources/Prototypes/Entities/Mobs/Customization/cyberlimbs/hesphiastos.yml +++ b/Resources/Prototypes/Entities/Mobs/Customization/cyberlimbs/hesphiastos.yml @@ -37,7 +37,7 @@ id: CyberLimbsMarkingHesphiastosLArm bodyPart: LArm markingCategory: LeftArm - speciesRestriction: [IPC, Moth, Dwarf, Human, Arachnid, Felinid, Oni, Vulpkanin, HumanoidFoxes, Reptilian, Arachne, Lamia] + speciesRestriction: [IPC, Moth, Dwarf, Human, Arachnid, Felinid, Oni, Vulpkanin, HumanoidFoxes, Reptilian, Arachne] sprites: - sprite: Mobs/Customization/cyberlimbs/hesphiastos/hesphiastos_main.rsi state: l_arm-1 @@ -48,7 +48,7 @@ id: CyberLimbsMarkingHesphiastosLHand bodyPart: LHand markingCategory: LeftHand - speciesRestriction: [IPC, Moth, Dwarf, Human, Arachnid, Felinid, Oni, Vulpkanin, HumanoidFoxes, Reptilian, Arachne, Lamia] + speciesRestriction: [IPC, Moth, Dwarf, Human, Arachnid, Felinid, Oni, Vulpkanin, HumanoidFoxes, Reptilian, Arachne] sprites: - sprite: Mobs/Customization/cyberlimbs/hesphiastos/hesphiastos_main.rsi state: l_hand-1 @@ -84,7 +84,7 @@ id: CyberLimbsMarkingHesphiastosRArm bodyPart: RArm markingCategory: RightArm - speciesRestriction: [IPC, Moth, Dwarf, Human, Arachnid, Felinid, Oni, Vulpkanin, HumanoidFoxes, Reptilian, Arachne, Lamia] + speciesRestriction: [IPC, Moth, Dwarf, Human, Arachnid, Felinid, Oni, Vulpkanin, HumanoidFoxes, Reptilian, Arachne] sprites: - sprite: Mobs/Customization/cyberlimbs/hesphiastos/hesphiastos_main.rsi state: r_arm-1 @@ -96,7 +96,7 @@ id: CyberLimbsMarkingHesphiastosRHand bodyPart: RHand markingCategory: RightHand - speciesRestriction: [IPC, Moth, Dwarf, Human, Arachnid, Felinid, Oni, Vulpkanin, HumanoidFoxes, Reptilian, Arachne, Lamia] + speciesRestriction: [IPC, Moth, Dwarf, Human, Arachnid, Felinid, Oni, Vulpkanin, HumanoidFoxes, Reptilian, Arachne] sprites: - sprite: Mobs/Customization/cyberlimbs/hesphiastos/hesphiastos_main.rsi state: r_hand-1 diff --git a/Resources/Prototypes/Entities/Mobs/Player/ipc.yml b/Resources/Prototypes/Entities/Mobs/Player/ipc.yml index 1f5fd9f20e..54c157cc3e 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/ipc.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/ipc.yml @@ -58,7 +58,7 @@ - !type:GibBehavior { } - type: SlowOnDamage speedModifierThresholds: - 60: 0.75 + 60: 0.7 90: 0.5 120: 0.3 - type: SiliconDownOnDead diff --git a/Resources/Prototypes/Entities/Mobs/Species/base.yml b/Resources/Prototypes/Entities/Mobs/Species/base.yml index a9d96ecde5..f740b89f42 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/base.yml @@ -90,8 +90,8 @@ species: Human - type: SlowOnDamage speedModifierThresholds: - 60: 0.75 - 80: 0.55 + 60: 0.8 # DV - Was 0.7 + 80: 0.6 # DV - Was 0.5 - type: Fixtures fixtures: # TODO: This needs a second fixture just for mob collisions. fix1: diff --git a/Resources/Prototypes/Entities/Objects/Misc/tiles.yml b/Resources/Prototypes/Entities/Objects/Misc/tiles.yml index 1eff8f9fc9..3d3675dd33 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/tiles.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/tiles.yml @@ -45,7 +45,7 @@ - type: DamageOnLand damage: types: - Blunt: 2 + Blunt: 5 - type: entity name: steel tile diff --git a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml index 468acc3851..cb556c7d4d 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml @@ -37,8 +37,6 @@ size: Large sprite: Objects/Specific/Janitorial/mop.rsi - type: Absorbent - footprintCleaningRange: 0.45 - maxCleanedFootprints: 25 - type: SolutionContainerManager solutions: absorbed: @@ -96,8 +94,6 @@ sprite: Objects/Specific/Janitorial/advmop.rsi - type: Absorbent pickupAmount: 100 - footprintCleaningRange: 0.75 - maxCleanedFootprints: 25 - type: UseDelay delay: 1.0 - type: SolutionRegeneration diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/morgue.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/morgue.yml index d79e637eef..da67b5a592 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/morgue.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/morgue.yml @@ -116,7 +116,6 @@ - type: Tag tags: - Trash - - Ash # Frontier - type: SolutionContainerManager solutions: food: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml index 6321e5a000..dcfd1537c7 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml @@ -55,7 +55,6 @@ damage: types: Piercing: 10 - staminaCost: 6 - type: Item size: Ginormous - type: Clothing @@ -113,6 +112,10 @@ max: 1 - !type:DoActsBehavior acts: [ "Destruction" ] + - type: DamageOnLand + damage: + types: + Blunt: 5 - type: UseDelay - type: Appearance - type: SolutionContainerVisuals diff --git a/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml b/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml index 8c3eeab6fa..d65d652ff4 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml @@ -47,7 +47,7 @@ sound: collection: MetalBreak - type: StaticPrice - price: 25 + price: 10 - type: RequireProjectileTarget #Starts unanchored, cannot be rotated while anchored diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index adf6bd94aa..75b593fbcc 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -1106,8 +1106,6 @@ - ClothingHeadHatBeretCmo - ClothingUniformJumpsuitCMO - ClothingUniformJumpskirtCMO - - ClothingUniformJumpsuitCMOTurtle - - ClothingUniformJumpskirtCMOTurtle - ClothingUniformJumpsuitDetective - ClothingUniformJumpskirtDetective - ClothingUniformJumpsuitEngineering diff --git a/Resources/Prototypes/Entities/Structures/base_structure.yml b/Resources/Prototypes/Entities/Structures/base_structure.yml index d143151deb..207881894b 100644 --- a/Resources/Prototypes/Entities/Structures/base_structure.yml +++ b/Resources/Prototypes/Entities/Structures/base_structure.yml @@ -28,10 +28,8 @@ - type: DamageOtherOnHit damage: types: - Blunt: 9 + Blunt: 8 staminaCost: 50 - soundHit: - collection: MetalThud - type: entity # This means that it's not anchored on spawn. diff --git a/Resources/Prototypes/Loadouts/Generic/outerClothing.yml b/Resources/Prototypes/Loadouts/Generic/outerClothing.yml index 029c90c8e3..57f8b35132 100644 --- a/Resources/Prototypes/Loadouts/Generic/outerClothing.yml +++ b/Resources/Prototypes/Loadouts/Generic/outerClothing.yml @@ -351,153 +351,3 @@ requirements: - !type:CharacterItemGroupRequirement group: LoadoutOuter - -- type: loadout - id: LoadoutOuterBomberAerostatic - category: Outer - cost: 0 - items: - - ClothingOuterCoatBomberAerostatic - requirements: - - !type:CharacterItemGroupRequirement - group: LoadoutOuter - -- type: loadout - id: LoadoutOuterBomberPunkRed - category: Outer - cost: 0 - items: - - ClothingOuterCoatBomberPunkRed - requirements: - - !type:CharacterItemGroupRequirement - group: LoadoutOuter - -- type: loadout - id: LoadoutOuterJacketLeather - category: Outer - cost: 0 - items: - - ClothingOuterCoatJacketLeather - requirements: - - !type:CharacterItemGroupRequirement - group: LoadoutOuter - -- type: loadout - id: LoadoutOuterJacketBiker - category: Outer - cost: 0 - items: - - ClothingOuterCoatJacketBiker - requirements: - - !type:CharacterItemGroupRequirement - group: LoadoutOuter - -- type: loadout - id: LoadoutOuterJacketJamrock - category: Outer - cost: 0 - items: - - ClothingOuterCoatJacketJamrock - requirements: - - !type:CharacterItemGroupRequirement - group: LoadoutOuter - -- type: loadout - id: LoadoutOuterJacketLettermanBlue - category: Outer - cost: 0 - items: - - ClothingOuterCoatJacketLettermanBlue - requirements: - - !type:CharacterItemGroupRequirement - group: LoadoutOuter - -- type: loadout - id: LoadoutOuterJacketLettermanBrown - category: Outer - cost: 0 - items: - - ClothingOuterCoatJacketLettermanBrown - requirements: - - !type:CharacterItemGroupRequirement - group: LoadoutOuter - -- type: loadout - id: LoadoutOuterJacketLettermanMagenta - category: Outer - cost: 0 - items: - - ClothingOuterCoatJacketLettermanMagenta - requirements: - - !type:CharacterItemGroupRequirement - group: LoadoutOuter - -- type: loadout - id: LoadoutOuterJacketLettermanRed - category: Outer - cost: 0 - items: - - ClothingOuterCoatJacketLettermanRed - requirements: - - !type:CharacterItemGroupRequirement - group: LoadoutOuter - -- type: loadout - id: LoadoutOuterBomberPinkSleeves - category: Outer - cost: 0 - items: - - ClothingOuterCoatBomberPinkSleeves - requirements: - - !type:CharacterItemGroupRequirement - group: LoadoutOuter - -- type: loadout - id: LoadoutOuterCoatAcidRaincoat - category: Outer - cost: 0 - items: - - ClothingOuterCoatAcidRaincoat - requirements: - - !type:CharacterItemGroupRequirement - group: LoadoutOuter - -- type: loadout - id: LoadoutOuterCoatAcidRaincoatBlue - category: Outer - cost: 0 - items: - - ClothingOuterCoatAcidRaincoatBlue - requirements: - - !type:CharacterItemGroupRequirement - group: LoadoutOuter - -- type: loadout - id: LoadoutOuterCoatAcidRaincoatGreen - category: Outer - cost: 0 - items: - - ClothingOuterCoatAcidRaincoatGreen - requirements: - - !type:CharacterItemGroupRequirement - group: LoadoutOuter - -- type: loadout - id: LoadoutOuterCoatAcidRaincoatMagenta - category: Outer - cost: 0 - items: - - ClothingOuterCoatAcidRaincoatMagenta - requirements: - - !type:CharacterItemGroupRequirement - group: LoadoutOuter - -- type: loadout - id: LoadoutOuterCoatAcidRaincoatYellow - category: Outer - cost: 0 - items: - - ClothingOuterCoatAcidRaincoatYellow - requirements: - - !type:CharacterItemGroupRequirement - group: LoadoutOuter diff --git a/Resources/Prototypes/Loadouts/Jobs/Command/captain.yml b/Resources/Prototypes/Loadouts/Jobs/Command/captain.yml index 02ed0324e3..b64ad384ef 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Command/captain.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Command/captain.yml @@ -355,19 +355,6 @@ items: - ClothingMaskGasCaptain -- type: loadout - id: LoadoutCommandCapMaskGasCombat - category: JobsCommandCaptain - cost: 0 - requirements: - - !type:CharacterItemGroupRequirement - group: LoadoutCaptainMask - - !type:CharacterJobRequirement - jobs: - - Captain - items: - - ClothingMaskGasCaptainCombat - # Outer - type: loadout id: LoadoutCommandCapOuterWinter @@ -395,21 +382,6 @@ items: - ClothingOuterArmorCaptainCarapace -- type: loadout - id: LoadoutCaptainOuterTrench - category: JobsCommandCaptain - cost: 0 - exclusive: true - canBeHeirloom: true - requirements: - - !type:CharacterItemGroupRequirement - group: LoadoutCaptainOuter - - !type:CharacterJobRequirement - jobs: - - Captain - items: - - ClothingOuterCoatCapTrench - # Shoes - type: loadout id: LoadoutCaptainShoesLaceup diff --git a/Resources/Prototypes/Loadouts/Jobs/Medical/chiefMedicalOfficer.yml b/Resources/Prototypes/Loadouts/Jobs/Medical/chiefMedicalOfficer.yml index 3910e7b9bb..b5e82749ce 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Medical/chiefMedicalOfficer.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Medical/chiefMedicalOfficer.yml @@ -53,19 +53,6 @@ items: - ClothingHeadHatBeretCmo -- type: loadout - id: LoadoutClothingHeadMirror - category: JobsMedicalChiefMedicalOfficer - cost: 0 - requirements: - - !type:CharacterItemGroupRequirement - group: LoadoutChiefMedicalOfficerHead - - !type:CharacterJobRequirement - jobs: - - ChiefMedicalOfficer - items: - - ClothingHeadMirror - # Id - type: loadout id: LoadoutChiefMedicalOfficerNTPDA @@ -225,29 +212,3 @@ - ChiefMedicalOfficer items: - ClothingUniformJumpskirtCMO - -- type: loadout - id: LoadoutChiefMedicalOfficerTurtleskirt - category: JobsMedicalChiefMedicalOfficer - cost: 0 - requirements: - - !type:CharacterItemGroupRequirement - group: LoadoutChiefMedicalOfficerUniforms - - !type:CharacterJobRequirement - jobs: - - ChiefMedicalOfficer - items: - - ClothingUniformJumpskirtCMOTurtle - -- type: loadout - id: LoadoutChiefMedicalOfficerTurtlesuit - category: JobsMedicalChiefMedicalOfficer - cost: 0 - requirements: - - !type:CharacterItemGroupRequirement - group: LoadoutChiefMedicalOfficerUniforms - - !type:CharacterJobRequirement - jobs: - - ChiefMedicalOfficer - items: - - ClothingUniformJumpsuitCMOTurtle diff --git a/Resources/Prototypes/Loadouts/loadout_groups.yml b/Resources/Prototypes/Loadouts/loadout_groups.yml deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/Resources/Prototypes/Nyanotrasen/Species/Oni.yml b/Resources/Prototypes/Nyanotrasen/Species/Oni.yml index 79d7e2b86f..160c1f3bec 100644 --- a/Resources/Prototypes/Nyanotrasen/Species/Oni.yml +++ b/Resources/Prototypes/Nyanotrasen/Species/Oni.yml @@ -21,6 +21,9 @@ - type: markingPoints id: MobOniMarkingLimits points: + Face: + points: 1 + required: false Hair: points: 1 required: false diff --git a/Resources/Prototypes/Nyanotrasen/Species/felinid.yml b/Resources/Prototypes/Nyanotrasen/Species/felinid.yml index ad92ea8046..31c224b37a 100644 --- a/Resources/Prototypes/Nyanotrasen/Species/felinid.yml +++ b/Resources/Prototypes/Nyanotrasen/Species/felinid.yml @@ -17,6 +17,9 @@ - type: markingPoints id: MobFelinidMarkingLimits points: + Face: + points: 1 + required: false Hair: points: 1 required: false diff --git a/Resources/Prototypes/Recipes/Lathes/clothing.yml b/Resources/Prototypes/Recipes/Lathes/clothing.yml index 98cb1a3d51..729f20e979 100644 --- a/Resources/Prototypes/Recipes/Lathes/clothing.yml +++ b/Resources/Prototypes/Recipes/Lathes/clothing.yml @@ -217,22 +217,6 @@ Cloth: 300 Durathread: 100 -- type: latheRecipe - id: ClothingUniformJumpsuitCMOTurtle - result: ClothingUniformJumpsuitCMOTurtle - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 - -- type: latheRecipe - id: ClothingUniformJumpskirtCMOTurtle - result: ClothingUniformJumpskirtCMOTurtle - completetime: 4 - materials: - Cloth: 300 - Durathread: 100 - - type: latheRecipe id: ClothingUniformJumpsuitDetective result: ClothingUniformJumpsuitDetective diff --git a/Resources/Prototypes/Species/arachne.yml b/Resources/Prototypes/Species/arachne.yml index fff75454fc..72e73fcf7f 100644 --- a/Resources/Prototypes/Species/arachne.yml +++ b/Resources/Prototypes/Species/arachne.yml @@ -15,6 +15,9 @@ - type: markingPoints id: MobArachneMarkingLimits points: + Face: + points: 1 + required: false Hair: points: 1 required: false diff --git a/Resources/Prototypes/Species/harpy.yml b/Resources/Prototypes/Species/harpy.yml index 1b61ebdd76..a131b23a7a 100644 --- a/Resources/Prototypes/Species/harpy.yml +++ b/Resources/Prototypes/Species/harpy.yml @@ -40,6 +40,9 @@ - type: markingPoints id: MobHarpyMarkingLimits points: + Face: + points: 1 + required: false Hair: points: 1 required: false diff --git a/Resources/Prototypes/Species/human.yml b/Resources/Prototypes/Species/human.yml index cc304a6eeb..6979bbcf91 100644 --- a/Resources/Prototypes/Species/human.yml +++ b/Resources/Prototypes/Species/human.yml @@ -39,6 +39,9 @@ - type: markingPoints id: MobHumanMarkingLimits points: + Face: + points: 1 + required: false Hair: points: 1 required: false diff --git a/Resources/Prototypes/Species/moth.yml b/Resources/Prototypes/Species/moth.yml index 5bba1b41d4..511f22394e 100644 --- a/Resources/Prototypes/Species/moth.yml +++ b/Resources/Prototypes/Species/moth.yml @@ -16,7 +16,6 @@ id: MobMothSprites sprites: Head: MobMothHead - Face: MobHumanoidAnyMarking Snout: MobHumanoidAnyMarking Chest: MobMothTorso HeadTop: MobHumanoidAnyMarking diff --git a/Resources/Prototypes/Species/reptilian.yml b/Resources/Prototypes/Species/reptilian.yml index c4ed49401b..99ea7f297a 100644 --- a/Resources/Prototypes/Species/reptilian.yml +++ b/Resources/Prototypes/Species/reptilian.yml @@ -42,6 +42,9 @@ id: MobReptilianMarkingLimits onlyWhitelisted: true points: + Face: + points: 1 + required: false Hair: points: 0 required: false @@ -60,7 +63,7 @@ points: 2 required: false HeadSide: - points: 3 + points: 2 required: false Chest: points: 1 diff --git a/Resources/Prototypes/Species/slime.yml b/Resources/Prototypes/Species/slime.yml index f0f383e75a..ada69c7500 100644 --- a/Resources/Prototypes/Species/slime.yml +++ b/Resources/Prototypes/Species/slime.yml @@ -31,6 +31,9 @@ - type: markingPoints id: MobSlimeMarkingLimits points: + Face: + points: 1 + required: false Hair: points: 1 required: false diff --git a/Resources/Prototypes/Traits/disabilities.yml b/Resources/Prototypes/Traits/disabilities.yml index 04c128eb8a..68877b01d2 100644 --- a/Resources/Prototypes/Traits/disabilities.yml +++ b/Resources/Prototypes/Traits/disabilities.yml @@ -83,7 +83,6 @@ jobs: - Borg - MedicalBorg - - Mime functions: - !type:TraitAddComponent components: @@ -142,30 +141,6 @@ components: - type: Snoring -- type: trait - id: BadKnees - category: Physical - points: 3 - requirements: - - !type:CharacterTraitRequirement - inverted: true - traits: - - ParkourTraining - - !type:CharacterSpeciesRequirement - inverted: true - species: - - Diona - functions: - - !type:TraitAddComponent - components: - - type: ClimbDelayModifier - climbDelayMultiplier: 1.5 - - type: SlippableModifier - paralyzeTimeMultiplier: 1.4 - - type: SpeedModifiedByContactModifier - walkModifierEffectiveness: 1.35 - sprintModifierEffectiveness: 1.35 - - type: trait id: Sluggish category: Physical @@ -184,9 +159,12 @@ - !type:TraitAddComponent components: - type: TraitSpeedModifier - sprintModifier: 0.84 - walkModifier: 0.84 - requiredTriggeredSpeedModifier: 0.84 + sprintModifier: 0.85 + walkModifier: 0.85 + - type: ClimbDelayModifier + climbDelayMultiplier: 1.35 + - type: LayingDownModifier + layingDownCooldownMultiplier: 1.2 - type: trait id: SnailPaced @@ -206,9 +184,12 @@ - !type:TraitAddComponent components: - type: TraitSpeedModifier - sprintModifier: 0.68 - walkModifier: 0.68 - requiredTriggeredSpeedModifier: 0.68 # Still slip against normal slips with the new sprint speed + sprintModifier: 0.7 + walkModifier: 0.7 + - type: ClimbDelayModifier + climbDelayMultiplier: 1.66 + - type: LayingDownModifier + layingDownCooldownMultiplier: 1.6 - type: trait id: BloodDeficiency diff --git a/Resources/Prototypes/Traits/inconveniences.yml b/Resources/Prototypes/Traits/inconveniences.yml index 62e13c2533..57d166d3e3 100644 --- a/Resources/Prototypes/Traits/inconveniences.yml +++ b/Resources/Prototypes/Traits/inconveniences.yml @@ -52,11 +52,6 @@ inverted: true traits: - Foreigner - - Muted - - !type:CharacterJobRequirement - inverted: true - jobs: - - Mime functions: - !type:TraitAddComponent components: @@ -73,11 +68,6 @@ inverted: true traits: - ForeignerLight - - Muted - - !type:CharacterJobRequirement - inverted: true - jobs: - - Mime functions: - !type:TraitAddComponent components: diff --git a/Resources/Prototypes/Traits/languages.yml b/Resources/Prototypes/Traits/languages.yml index 44a487cecd..a8bdeff468 100644 --- a/Resources/Prototypes/Traits/languages.yml +++ b/Resources/Prototypes/Traits/languages.yml @@ -1,7 +1,7 @@ - type: trait id: SignLanguage category: TraitsSpeechLanguages - points: -2 + points: 0 requirements: - !type:CharacterItemGroupRequirement group: TraitsLanguagesBasic @@ -79,7 +79,7 @@ - type: trait id: ValyrianStandard category: TraitsSpeechLanguages - points: -1 + points: 1 requirements: - !type:CharacterItemGroupRequirement group: TraitsLanguagesBasic @@ -93,7 +93,7 @@ - type: trait id: Azaziba category: TraitsSpeechLanguages - points: -1 + points: 1 requirements: - !type:CharacterSpeciesRequirement species: diff --git a/Resources/Prototypes/Traits/physical.yml b/Resources/Prototypes/Traits/physical.yml index 3f3d460f99..f2f071a309 100644 --- a/Resources/Prototypes/Traits/physical.yml +++ b/Resources/Prototypes/Traits/physical.yml @@ -235,52 +235,6 @@ - type: PainTolerance rangeModifier: 0.6 -- type: trait - id: Steadfast - category: Physical - points: -4 - requirements: - - !type:CharacterJobRequirement - inverted: true - jobs: - - Borg - - MedicalBorg - - !type:CharacterSpeciesRequirement - inverted: true - species: - - Felinid - - !type:CharacterTraitRequirement - inverted: true - traits: - - Feeble - functions: - - !type:TraitModifySlowOnDamage - damageThresholdsModifier: 10 - speedModifierMultiplier: 0.68 - -- type: trait - id: Feeble - category: Physical - points: 3 - requirements: - - !type:CharacterJobRequirement - inverted: true - jobs: - - Borg - - MedicalBorg - - !type:CharacterSpeciesRequirement - inverted: true - species: - - Felinid - - !type:CharacterTraitRequirement - inverted: true - traits: - - Steadfast - functions: - - !type:TraitModifySlowOnDamage - damageThresholdsModifier: -15 - speedModifierMultiplier: 1.2 - - type: trait id: MartialArtist category: Physical diff --git a/Resources/Prototypes/Traits/skills.yml b/Resources/Prototypes/Traits/skills.yml index 8a035d24f6..25603c7347 100644 --- a/Resources/Prototypes/Traits/skills.yml +++ b/Resources/Prototypes/Traits/skills.yml @@ -135,7 +135,6 @@ traits: - Sluggish - SnailPaced - - BadKnees - !type:CharacterSpeciesRequirement inverted: true species: @@ -144,12 +143,10 @@ - !type:TraitReplaceComponent components: - type: ClimbDelayModifier - climbDelayMultiplier: 0.5 - - type: SlippableModifier - paralyzeTimeMultiplier: 0.7 - - type: SpeedModifiedByContactModifier - walkModifierEffectiveness: 0.5 - sprintModifierEffectiveness: 0.5 + climbDelayMultiplier: 0.35 + - type: LayingDownModifier + layingDownCooldownMultiplier: 0.5 + downedSpeedMultiplierMultiplier: 1.65 - type: trait id: LightStep diff --git a/Resources/Prototypes/_NF/Entities/Clothing/Belt/belts_punk.yml b/Resources/Prototypes/_NF/Entities/Clothing/Belt/belts_punk.yml deleted file mode 100644 index 5dcaa8b8c8..0000000000 --- a/Resources/Prototypes/_NF/Entities/Clothing/Belt/belts_punk.yml +++ /dev/null @@ -1,45 +0,0 @@ - -- type: entity - parent: ClothingBeltStorageBase - id: ClothingBeltPunkRandomized - name: punk belt - description: A webbing with functional pockets. - suffix: Random visuals - components: - - type: Sprite - sprite: _NF/Clothing/Belt/punk.rsi - layers: - - state: base_belt_01 - map: [ "belt_main" ] - scale: 1.8, 1.8 - offset: 0,0.1 - - state: decor_base_belt_01 - map: [ "belt_decor" ] - scale: 1.8, 1.8 - offset: 0,0.1 - - state: mask_null - map: [ "belt_overlay" ] - scale: 1.8, 1.8 - offset: 0,0.1 - - type: Clothing - sprite: _NF/Clothing/Belt/punk.rsi - clothingVisuals: - belt: - - state: base_belt_01 - map: [ "belt_main" ] - - state: decor_base_belt_01 - map: [ "belt_decor" ] - - state: mask_null - map: [ "belt_overlay" ] - - type: RandomSprite - available: - - belt_main: - base_belt_01: CyberpunkDark - belt_decor: - decor_base_belt_01: CyberpunkDark - belt_overlay: - decor_base_belt_02: CyberpunkDark - decor_base_belt_03: CyberpunkDark - decor_base_belt_04: CyberpunkDark - decor_base_belt_05: CyberpunkDark - mask_null: "" diff --git a/Resources/Prototypes/_NF/Entities/Clothing/Eyes/punk_glasses.yml b/Resources/Prototypes/_NF/Entities/Clothing/Eyes/punk_glasses.yml deleted file mode 100644 index 34e58e8904..0000000000 --- a/Resources/Prototypes/_NF/Entities/Clothing/Eyes/punk_glasses.yml +++ /dev/null @@ -1,78 +0,0 @@ -# Glasses -- type: entity - parent: ClothingEyesGlassesMeson - id: ClothingEyesPunkGoggles - name: punk goggles - description: How can you see anything in this with all the lights? - components: - - type: VisionCorrection - - type: Sprite - sprite: _NF/Clothing/Eyes/Glasses/punk_glasses.rsi - layers: - - state: base_glasses_01 - map: [ "glasses_main" ] - scale: 2.5, 2.5 - offset: 0,-0.6 - - state: decor_base_glasses_01 - map: [ "glasses_decor" ] - scale: 2.5, 2.5 - offset: 0,-0.6 - shader: unshaded - - type: Clothing - sprite: _NF/Clothing/Eyes/Glasses/punk_glasses.rsi - clothingVisuals: - eyes: - - state: base_glasses_01 - map: [ "glasses_main" ] - - state: decor_base_glasses_01 - map: [ "glasses_decor" ] - shader: unshaded - - type: RandomSprite - available: - - glasses_main: - base_glasses_01: CyberpunkDark - glasses_decor: - decor_base_glasses_01: CyberpunkDark - -# HUDs -- type: entity - parent: ClothingEyesHudMedical - id: ClothingEyesPunkInfoShades - name: punk infoshades - description: How can you see anything in this with all the lights? - components: - - type: Sprite - sprite: _NF/Clothing/Eyes/Glasses/punk_glasses.rsi - layers: - - state: base_glasses_02 - map: [ "infoshades_main" ] - scale: 2.5, 2.5 - offset: 0,-0.6 - - state: decor_base_glasses_02 - map: [ "infoshades_decor" ] - scale: 2.5, 2.5 - offset: 0,-0.6 - shader: unshaded - - type: Clothing - sprite: _NF/Clothing/Eyes/Glasses/punk_glasses.rsi - clothingVisuals: - eyes: - - state: base_glasses_02 - map: [ "infoshades_main" ] - - state: decor_base_glasses_02 - map: [ "infoshades_decor" ] - shader: unshaded - - type: RandomSprite - available: - - infoshades_main: - base_glasses_02: CyberpunkNeon - infoshades_decor: - decor_base_glasses_02: CyberpunkNeon - - type: ShowHealthBars - damageContainers: - - Inorganic - - Silicon - - Biological - - type: ShowHealthIcons - damageContainers: - - Biological diff --git a/Resources/Prototypes/_NF/Entities/Clothing/Head/headwear_punks.yml b/Resources/Prototypes/_NF/Entities/Clothing/Head/headwear_punks.yml deleted file mode 100644 index 9bade6e0f1..0000000000 --- a/Resources/Prototypes/_NF/Entities/Clothing/Head/headwear_punks.yml +++ /dev/null @@ -1,59 +0,0 @@ -# Acid raincoat hoods -- type: entity - parent: ClothingHeadBase - id: ClothingHeadHatHoodAcidRaincoat - categories: [ HideSpawnMenu ] - name: acid raincoat hood - description: Mostly transparent raincoat hood made out of acid resistant polymers. - components: - - type: Sprite - sprite: _NF/Clothing/Head/Hoods/Coat/acid_raincoat.rsi - - type: Clothing - sprite: _NF/Clothing/Head/Hoods/Coat/acid_raincoat.rsi - - type: Armor - modifiers: - coefficients: - Caustic: 0.9 - - type: HideLayerClothing - slots: - - Hair - -- type: entity - parent: ClothingHeadHatHoodAcidRaincoat - id: ClothingHeadHatHoodAcidRaincoatBlue - categories: [ HideSpawnMenu ] - components: - - type: Sprite - sprite: _NF/Clothing/Head/Hoods/Coat/acid_raincoat_blue.rsi - - type: Clothing - sprite: _NF/Clothing/Head/Hoods/Coat/acid_raincoat_blue.rsi - -- type: entity - parent: ClothingHeadHatHoodAcidRaincoat - id: ClothingHeadHatHoodAcidRaincoatGreen - categories: [ HideSpawnMenu ] - components: - - type: Sprite - sprite: _NF/Clothing/Head/Hoods/Coat/acid_raincoat_green.rsi - - type: Clothing - sprite: _NF/Clothing/Head/Hoods/Coat/acid_raincoat_green.rsi - -- type: entity - parent: ClothingHeadHatHoodAcidRaincoat - id: ClothingHeadHatHoodAcidRaincoatMagenta - categories: [ HideSpawnMenu ] - components: - - type: Sprite - sprite: _NF/Clothing/Head/Hoods/Coat/acid_raincoat_magenta.rsi - - type: Clothing - sprite: _NF/Clothing/Head/Hoods/Coat/acid_raincoat_magenta.rsi - -- type: entity - parent: ClothingHeadHatHoodAcidRaincoat - id: ClothingHeadHatHoodAcidRaincoatYellow - categories: [ HideSpawnMenu ] - components: - - type: Sprite - sprite: _NF/Clothing/Head/Hoods/Coat/acid_raincoat_yellow.rsi - - type: Clothing - sprite: _NF/Clothing/Head/Hoods/Coat/acid_raincoat_yellow.rsi diff --git a/Resources/Prototypes/_NF/Entities/Clothing/Masks/masks_punk.yml b/Resources/Prototypes/_NF/Entities/Clothing/Masks/masks_punk.yml deleted file mode 100644 index 3b05bbd48c..0000000000 --- a/Resources/Prototypes/_NF/Entities/Clothing/Masks/masks_punk.yml +++ /dev/null @@ -1,36 +0,0 @@ - -- type: entity - parent: ClothingMaskPullableBase - id: ClothingMaskPunkHalf - name: punk halfmask - description: A close-fitting breathing mask with gamer lights! - suffix: Random visuals - components: - - type: BreathMask - - type: Sprite - sprite: _NF/Clothing/Mask/punkhalfmask.rsi - layers: - - state: base_halfmask_01 - map: [ "mask_main" ] - scale: 2, 2 - offset: 0,-0.6 - - state: decor_base_halfmask_01 - map: [ "mask_decor" ] - shader: unshaded - scale: 2, 2 - offset: 0,-0.6 - - type: Clothing - sprite: _NF/Clothing/Mask/punkhalfmask.rsi - clothingVisuals: - mask: - - state: base_halfmask_01 - map: [ "mask_main" ] - - state: decor_base_halfmask_01 - map: [ "mask_decor" ] - shader: unshaded - - type: RandomSprite - available: - - mask_main: - base_halfmask_01: CyberpunkDark - mask_decor: - decor_base_halfmask_01: CyberpunkNeon diff --git a/Resources/Prototypes/_NF/Entities/Clothing/Neck/misc.yml b/Resources/Prototypes/_NF/Entities/Clothing/Neck/misc.yml deleted file mode 100644 index 097994e97d..0000000000 --- a/Resources/Prototypes/_NF/Entities/Clothing/Neck/misc.yml +++ /dev/null @@ -1,17 +0,0 @@ -- type: entity - parent: ClothingNeckBase - id: ClothingNeckCrucifix - name: crucifix - description: Damn, it feels good to be so pious. - components: - - type: Item - size: Small - - type: Sprite - sprite: _NF/Clothing/Neck/Misc/crucifix.rsi - - type: Clothing - sprite: _NF/Clothing/Neck/Misc/crucifix.rsi - - type: ReactionMixer # I'm assuming that this is used to make holy water - mixMessage: "bible-mixing-success" - reactionTypes: - - Holy - - type: Tag diff --git a/Resources/Prototypes/_NF/Entities/Clothing/Neck/scarfs.yml b/Resources/Prototypes/_NF/Entities/Clothing/Neck/scarfs.yml deleted file mode 100644 index bed54cc70d..0000000000 --- a/Resources/Prototypes/_NF/Entities/Clothing/Neck/scarfs.yml +++ /dev/null @@ -1,10 +0,0 @@ -- type: entity - parent: ClothingNeckBase - id: ClothingNeckScarfChaplainStole - name: chaplain's stole - description: A necessary evil for ordained priests outfit. Gives at least +2 to your holiness. - components: - - type: Sprite - sprite: _NF/Clothing/Neck/Scarfs/chaplain_stole.rsi - - type: Clothing - sprite: _NF/Clothing/Neck/Scarfs/chaplain_stole.rsi diff --git a/Resources/Prototypes/_NF/Entities/Clothing/OuterClothing/armor_punk.yml b/Resources/Prototypes/_NF/Entities/Clothing/OuterClothing/armor_punk.yml deleted file mode 100644 index 94f06de3c8..0000000000 --- a/Resources/Prototypes/_NF/Entities/Clothing/OuterClothing/armor_punk.yml +++ /dev/null @@ -1,141 +0,0 @@ -#Mercenary web vest -- type: entity - parent: [ClothingOuterStorageBase, AllowSuitStorageClothing] #web vest so it should have some pockets for ammo # Frontier: ClothingOuterVestWeb