diff --git a/Content.Client/Lobby/LobbyState.cs b/Content.Client/Lobby/LobbyState.cs index bed52217a99..39186ed1f41 100644 --- a/Content.Client/Lobby/LobbyState.cs +++ b/Content.Client/Lobby/LobbyState.cs @@ -67,8 +67,10 @@ protected override void Startup() _characterSetup.SaveButton.OnPressed += _ => { - _characterSetup.Save(); + // _lobby.CharacterPreview.UpdateUI(); + // _characterSetup.Save(); _userInterfaceManager.GetUIController().UpdateCharacterUI(); + // _characterSetup.Save(); //Nuclear14 workaround to prevent saving using this button, as I can't make it disable dependent on Special points }; LayoutContainer.SetAnchorPreset(_lobby, LayoutContainer.LayoutPreset.Wide); diff --git a/Content.Client/Preferences/UI/HumanoidProfileEditor.xaml b/Content.Client/Preferences/UI/HumanoidProfileEditor.xaml index ebf794954e3..b2a75381ef4 100644 --- a/Content.Client/Preferences/UI/HumanoidProfileEditor.xaml +++ b/Content.Client/Preferences/UI/HumanoidProfileEditor.xaml @@ -189,6 +189,14 @@ + + + diff --git a/Content.Client/Preferences/UI/HumanoidProfileEditor.xaml.cs b/Content.Client/Preferences/UI/HumanoidProfileEditor.xaml.cs index 771d928ebf9..63c2ac86901 100644 --- a/Content.Client/Preferences/UI/HumanoidProfileEditor.xaml.cs +++ b/Content.Client/Preferences/UI/HumanoidProfileEditor.xaml.cs @@ -6,6 +6,7 @@ using Content.Client.Message; using Content.Client.Players.PlayTimeTracking; using Content.Client.Roles; +using Content.Client.Stylesheets; using Content.Client.UserInterface.Systems.Guidebook; using Content.Shared.CCVar; using Content.Shared.Clothing.Loadouts.Prototypes; @@ -18,6 +19,7 @@ using Content.Shared.Preferences; using Content.Shared.Roles; using Content.Shared.Roles.Jobs; +using Content.Shared.StatusIcon; using Content.Shared.Traits; using Robust.Client.AutoGenerated; using Robust.Client.Graphics; @@ -33,6 +35,8 @@ using Robust.Shared.Prototypes; using Robust.Shared.Utility; using Direction = Robust.Shared.Maths.Direction; +using Content.Shared.Nuclear14.Special; +using Content.Shared.Nuclear14.CCVar; namespace Content.Client.Preferences.UI { @@ -69,6 +73,9 @@ public sealed partial class HumanoidProfileEditor : BoxContainer private TabContainer _tabContainer => CTabContainer; private BoxContainer _jobList => CJobList; + private Label _specialPointsLabel => SpecialPointsLabel; // Nuclear14 special bar points label + private ProgressBar _specialPointsBar => SpecialPointsBar; // Nuclear14 The above labels' names are referencing their position relative to this element + private BoxContainer _specialList => CSpecialList; // Nuclear14 Special list private BoxContainer _antagList => CAntagList; private Label _traitPointsLabel => TraitPointsLabel; private int _traitCount; @@ -82,6 +89,7 @@ public sealed partial class HumanoidProfileEditor : BoxContainer private BoxContainer _loadoutsTab => CLoadoutsTab; private TabContainer _loadoutsTabs => CLoadoutsTabs; private readonly List _jobPriorities; + private readonly List _specialPriorities; // Nuclear14 User special choice private OptionButton _preferenceUnavailableButton => CPreferenceUnavailableButton; private readonly Dictionary _jobCategories; private readonly List _speciesList; @@ -490,6 +498,16 @@ public HumanoidProfileEditor(IClientPreferencesManager preferencesManager, IProt #endregion Jobs + // Nuclear14 Special + #region Specials + + _tabContainer.SetTabTitle(5, Loc.GetString("humanoid-profile-editor-specials-tab")); + _specialPriorities = new List(); + UpdateSpecialRequirements(); + + #endregion Specials + // Nuclear14 end + #region Antags _tabContainer.SetTabTitle(2, Loc.GetString("humanoid-profile-editor-antags-tab")); @@ -783,6 +801,56 @@ private void EnsureJobRequirementsValid() Save(); } + // Nuclear14 update Special select requirments + private void UpdateSpecialRequirements() + { + _specialList.DisposeAllChildren(); + _specialPriorities.Clear(); + + var points = _configurationManager.GetCVar(SpecialCCVars.MaxSpecial); + _specialPointsLabel.Text = Loc.GetString("humanoid-profile-editor-loadouts-points-label", ("points", points), ("max", points)); + _specialPointsBar.MaxValue = points; + _specialPointsBar.Value = points; + + + foreach (var special in _prototypeManager.EnumeratePrototypes().OrderBy(a => a.Order)) + { + var selector = new SpecialPrioritySelector(special, _prototypeManager); + + _specialList.AddChild(selector); + _specialPriorities.Add(selector); + + selector.PriorityChanged += priority => + { + foreach (var specialSelector in _specialPriorities) + { + + if(priority != 0) + { + var temp = _specialPointsBar.Value - (int) priority; + if (temp < 0){ + } + else + { + _specialPointsLabel.Text = Loc.GetString("humanoid-profile-editor-special-points-label", ("points", _specialPointsBar.Value), ("max", _specialPointsBar.MaxValue)); + _specialPointsBar.Value = temp; + } + } + else + { + _specialPointsLabel.Text = Loc.GetString("humanoid-profile-editor-special-points-label", ("points", _specialPointsBar.Value), ("max", _specialPointsBar.MaxValue)); + _specialPointsBar.Value += (int) specialSelector.Priority; + } + Profile = Profile?.WithSpecialPriority(special.ID, priority); + IsDirty = true; + + UpdateSpecialPriorities(); + } + }; + + } + } + // Nuclear14 end private void OnFlavorTextChange(string content) { @@ -1317,6 +1385,7 @@ public void UpdateControls() UpdateEyePickers(); UpdateSaveButton(); UpdateJobPriorities(); + UpdateSpecialPriorities(); // Nuclear14 Special Priorities UpdateAntagPreferences(); UpdateTraitPreferences(); UpdateLoadouts(_loadoutsShowUnusableButton.Pressed); @@ -1344,6 +1413,124 @@ private void UpdateJobPriorities() } } + // Nuclear14 Special + private void UpdateSpecialPriorities() + { + var points = _configurationManager.GetCVar(SpecialCCVars.MaxSpecial); + _specialPointsBar.Value = points; + _specialPointsLabel.Text = Loc.GetString("humanoid-profile-editor-special-points-label", ("points", _specialPointsBar.Value), ("max", _specialPointsBar.MaxValue)); + + foreach (var prioritySelector in _specialPriorities) + { + var specialId = prioritySelector.Special.ID; + + var priority = Profile?.SpecialPriorities.GetValueOrDefault(specialId, SpecialPriority.Zero) ?? SpecialPriority.Zero; + + prioritySelector.Priority = priority; + + if (priority != SpecialPriority.Zero) + { + points -= (int) priority; + _specialPointsBar.Value = points; + _specialPointsLabel.Text = Loc.GetString("humanoid-profile-editor-special-points-label", ("points", points), ("max", _specialPointsBar.MaxValue)); + + } + } + if (points < 0) + _saveButton.Disabled = true; + + } + + private sealed class SpecialPrioritySelector : Control + { + public SpecialPrototype Special { get; } + private readonly RadioOptions _optionButton; + + public SpecialPriority Priority + { + get => (SpecialPriority) _optionButton.SelectedValue; + set => _optionButton.SelectByValue((int) value); + } + + public event Action? PriorityChanged; + private Label _specialTitle; + + public SpecialPrioritySelector(SpecialPrototype special, IPrototypeManager prototypeManager) + { + Special = special; + + _optionButton = new RadioOptions(RadioOptionsLayout.Horizontal) + { + FirstButtonStyle = StyleBase.ButtonOpenRight, + ButtonStyle = StyleBase.ButtonOpenBoth, + LastButtonStyle = StyleBase.ButtonOpenLeft + }; + //Override default radio option button width + _optionButton.GenerateItem = GenerateButton; + // Text, Value + _optionButton.AddItem(Loc.GetString("humanoid-profile-editor-special-priority-one-button"), (int) SpecialPriority.One); + _optionButton.AddItem(Loc.GetString("humanoid-profile-editor-special-priority-two-button"), (int) SpecialPriority.Two); + _optionButton.AddItem(Loc.GetString("humanoid-profile-editor-special-priority-three-button"), (int) SpecialPriority.Three); + _optionButton.AddItem(Loc.GetString("humanoid-profile-editor-special-priority-four-button"), (int) SpecialPriority.Four); + _optionButton.AddItem(Loc.GetString("humanoid-profile-editor-special-priority-five-button"), (int) SpecialPriority.Five); + _optionButton.AddItem(Loc.GetString("humanoid-profile-editor-special-priority-six-button"), (int) SpecialPriority.Six); + _optionButton.AddItem(Loc.GetString("humanoid-profile-editor-special-priority-seven-button"), (int) SpecialPriority.Seven); + _optionButton.AddItem(Loc.GetString("humanoid-profile-editor-special-priority-eight-button"), (int) SpecialPriority.Eight); + _optionButton.AddItem(Loc.GetString("humanoid-profile-editor-special-priority-nine-button"), (int) SpecialPriority.Nine); + _optionButton.AddItem(Loc.GetString("humanoid-profile-editor-special-priority-ten-button"), (int) SpecialPriority.Ten); + + _optionButton.OnItemSelected += args => + { + _optionButton.Select(args.Id); + PriorityChanged?.Invoke(Priority); + }; + + var icon = new TextureRect + { + TextureScale = new Vector2(2, 2), + Stretch = TextureRect.StretchMode.KeepCentered + }; + + var specialIcon = prototypeManager.Index(special.Icon); + icon.Texture = specialIcon.Icon.Frame0(); + + _specialTitle = new Label() + { + Margin = new Thickness(5f,5f,5f,5f), + Text = special.LocalizedName, + MinSize = new Vector2(100, 0), + MouseFilter = MouseFilterMode.Stop + }; + + if (special.LocalizedDescription != null) + { + _specialTitle.ToolTip = special.LocalizedDescription; + _specialTitle.TooltipDelay = 0.2f; + } + + AddChild(new BoxContainer + { + Orientation = LayoutOrientation.Horizontal, + Children = + { + icon, + _specialTitle, + _optionButton, + } + }); + } + private Button GenerateButton(string text, int value) + { + var btn = new Button + { + Text = text, + MinWidth = 40 + }; + return btn; + } + } + // Nuclear14 end + private void UpdateAntagPreferences() { foreach (var preferenceSelector in _antagPreferences) diff --git a/Content.Client/Weapons/Ranged/GunSpreadOverlay.cs b/Content.Client/Weapons/Ranged/GunSpreadOverlay.cs index 62df764ae50..c7fc12755d6 100644 --- a/Content.Client/Weapons/Ranged/GunSpreadOverlay.cs +++ b/Content.Client/Weapons/Ranged/GunSpreadOverlay.cs @@ -5,6 +5,7 @@ using Robust.Shared.Enums; using Robust.Shared.Map; using Robust.Shared.Timing; +using Content.Shared.Nuclear14.Special.Components; namespace Content.Client.Weapons.Ranged; @@ -58,9 +59,25 @@ protected override void Draw(in OverlayDrawArgs args) // (☞゚ヮ゚)☞ var maxSpread = gun.MaxAngleModified; var minSpread = gun.MinAngleModified; + // Nuclear14 Shotting accuracy depends on Perception + var decay = gun.AngleDecayModified; var timeSinceLastFire = (_timing.CurTime - gun.NextFire).TotalSeconds; - var currentAngle = new Angle(MathHelper.Clamp(gun.CurrentAngle.Theta - gun.AngleDecayModified.Theta * timeSinceLastFire, - gun.MinAngleModified.Theta, gun.MaxAngleModified.Theta)); + Angle currentAngle; + if (_entManager.TryGetComponent(player, out var special)) + { + maxSpread += Angle.FromDegrees((40f - special.TotalPerception * 5)); + minSpread += Angle.FromDegrees((10f - special.TotalPerception)); + maxSpread *= 1.5 - special.TotalPerception / 10; + minSpread *= 1.5 - special.TotalPerception / 10; + decay += Angle.FromDegrees((special.TotalPerception - 10f) / 5); + + currentAngle = new Angle(MathHelper.Clamp(gun.CurrentAngle.Theta - decay.Theta * timeSinceLastFire, + minSpread.Theta, maxSpread.Theta)); + } + else + currentAngle = new Angle(MathHelper.Clamp(gun.CurrentAngle.Theta - gun.AngleDecayModified.Theta * timeSinceLastFire, + gun.MinAngleModified.Theta, gun.MaxAngleModified.Theta)); + // Nuclear14 end var direction = (mousePos.Position - mapPos.Position); worldHandle.DrawLine(mapPos.Position, mousePos.Position + direction, Color.Orange); diff --git a/Content.IntegrationTests/Tests/Preferences/ServerDbSqliteTests.cs b/Content.IntegrationTests/Tests/Preferences/ServerDbSqliteTests.cs index b4417200242..d759a78215d 100644 --- a/Content.IntegrationTests/Tests/Preferences/ServerDbSqliteTests.cs +++ b/Content.IntegrationTests/Tests/Preferences/ServerDbSqliteTests.cs @@ -4,6 +4,7 @@ using Content.Shared.GameTicking; using Content.Shared.Humanoid; using Content.Shared.Preferences; +using Content.Shared.Nuclear14.Special; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using Robust.Shared.Configuration; @@ -65,7 +66,17 @@ private static HumanoidCharacterProfile CharlieCharlieson() PreferenceUnavailableMode.StayInLobby, antagPreferences: new List(), traitPreferences: new List(), - loadoutPreferences: new List() + loadoutPreferences: new List(), + specialPriorities: new Dictionary + { + {"Strength", SpecialPriority.Five}, + {"Perception", SpecialPriority.Five}, + {"Endurance", SpecialPriority.Five}, + {"Charisma", SpecialPriority.Five}, + {"Intelligence", SpecialPriority.Five}, + {"Agility", SpecialPriority.Five}, + {"Luck", SpecialPriority.Five} + } ); } diff --git a/Content.Server.Database/Migrations/Postgres/20240215030858_Special.Designer.cs b/Content.Server.Database/Migrations/Postgres/20240215030858_Special.Designer.cs new file mode 100644 index 00000000000..569a3764f2c --- /dev/null +++ b/Content.Server.Database/Migrations/Postgres/20240215030858_Special.Designer.cs @@ -0,0 +1,1810 @@ +// +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; + +#nullable disable + +namespace Content.Server.Database.Migrations.Postgres +{ + [DbContext(typeof(PostgresServerDbContext))] + [Migration("20240215030858_Special")] + partial class Special + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.4") + .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("Id") + .HasColumnType("integer") + .HasColumnName("admin_log_id"); + + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_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("Id", "RoundId") + .HasName("PK_admin_log"); + + b.HasIndex("Date"); + + b.HasIndex("Message") + .HasAnnotation("Npgsql:TsVectorConfig", "english"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Message"), "GIN"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_log_round_id"); + + b.HasIndex("Type") + .HasDatabaseName("IX_admin_log_type"); + + b.ToTable("admin_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLogEntity", b => + { + b.Property("Uid") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("uid"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Uid")); + + b.Property("AdminLogId") + .HasColumnType("integer") + .HasColumnName("admin_log_id"); + + b.Property("AdminLogRoundId") + .HasColumnType("integer") + .HasColumnName("admin_log_round_id"); + + b.Property("Name") + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Uid") + .HasName("PK_admin_log_entity"); + + b.HasIndex("AdminLogId", "AdminLogRoundId") + .HasDatabaseName("IX_admin_log_entity_admin_log_id_admin_log_round_id"); + + b.ToTable("admin_log_entity", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b => + { + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("LogId") + .HasColumnType("integer") + .HasColumnName("log_id"); + + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.HasKey("PlayerUserId", "LogId", "RoundId") + .HasName("PK_admin_log_player"); + + b.HasIndex("LogId", "RoundId"); + + 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("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", (string)null); + }); + + 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.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("HWId") + .HasColumnType("bytea") + .HasColumnName("hwid"); + + 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("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.Special", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("special_id"); + + b.Property("SpecialName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("special_name"); + + b.Property("Priority") + .HasColumnType("INTEGER") + .HasColumnName("priority"); + + b.Property("ProfileId") + .HasColumnType("INTEGER") + .HasColumnName("profile_id"); + + b.HasKey("Id") + .HasName("PK_special"); + + b.HasIndex("ProfileId"); + + b.HasIndex("ProfileId", "SpecialName") + .IsUnique(); + + b.ToTable("special", (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("LastSeenHWId") + .HasColumnType("bytea") + .HasColumnName("last_seen_hwid"); + + 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("Backpack") + .IsRequired() + .HasColumnType("text") + .HasColumnName("backpack"); + + b.Property("CharacterName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("char_name"); + + b.Property("Clothing") + .IsRequired() + .HasColumnType("text") + .HasColumnName("clothing"); + + 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("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.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.HasKey("Id") + .HasName("PK_round"); + + b.HasIndex("ServerId") + .HasDatabaseName("IX_round_server_id"); + + 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("HWId") + .HasColumnType("bytea") + .HasColumnName("hwid"); + + 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("HWId") + .HasColumnType("bytea") + .HasColumnName("hwid"); + + 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.AdminLogEntity", b => + { + b.HasOne("Content.Server.Database.AdminLog", null) + .WithMany("Entities") + .HasForeignKey("AdminLogId", "AdminLogRoundId") + .HasConstraintName("FK_admin_log_entity_admin_log_admin_log_id_admin_log_round_id"); + }); + + 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("LogId", "RoundId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_player_admin_log_log_id_round_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.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.Special", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Specials") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_special_profile_profile_id"); + + b.Navigation("Profile"); + }); + + 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.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.Navigation("CreatedBy"); + + 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.Navigation("CreatedBy"); + + 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("Entities"); + + 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"); + }); + + 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("Traits"); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.Navigation("AdminLogs"); + }); + + modelBuilder.Entity("Content.Server.Database.Server", b => + { + 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/20240215030858_Special.cs b/Content.Server.Database/Migrations/Postgres/20240215030858_Special.cs new file mode 100644 index 00000000000..a0898d32884 --- /dev/null +++ b/Content.Server.Database/Migrations/Postgres/20240215030858_Special.cs @@ -0,0 +1,45 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Content.Server.Database.Migrations.Postgres +{ + public partial class Special : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "special", + columns: table => new + { + special_id = table.Column(nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + profile_id = table.Column(nullable: false), + special_name = table.Column(nullable: false), + priority = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_special", x => x.special_id); + table.ForeignKey( + name: "FK_special_profile_profile_id", + column: x => x.profile_id, + principalTable: "profile", + principalColumn: "profile_id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_special_profile_id", + table: "special", + column: "profile_id"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "special"); + } + } +} diff --git a/Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs b/Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs index 8fbcbf28356..441df105a9f 100644 --- a/Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs +++ b/Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs @@ -631,6 +631,38 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("loadout", (string)null); }); + + modelBuilder.Entity("Content.Server.Database.Special", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("special_id"); + + b.Property("SpecialName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("special_name"); + + b.Property("Priority") + .HasColumnType("INTEGER") + .HasColumnName("priority"); + + b.Property("ProfileId") + .HasColumnType("INTEGER") + .HasColumnName("profile_id"); + + b.HasKey("Id") + .HasName("PK_special"); + + b.HasIndex("ProfileId"); + + b.HasIndex("ProfileId", "SpecialName") + .IsUnique(); + + b.ToTable("special", (string)null); + }); + modelBuilder.Entity("Content.Server.Database.PlayTime", b => { b.Property("Id") @@ -1556,6 +1588,18 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Profile"); }); + modelBuilder.Entity("Content.Server.Database.Special", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Specials") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_special_profile_profile_id"); + + b.Navigation("Profile"); + }); + modelBuilder.Entity("Content.Server.Database.Profile", b => { b.HasOne("Content.Server.Database.Preference", "Preference") diff --git a/Content.Server.Database/Migrations/Sqlite/20240215030858_Special.Designer.cs b/Content.Server.Database/Migrations/Sqlite/20240215030858_Special.Designer.cs new file mode 100644 index 00000000000..251269752bc --- /dev/null +++ b/Content.Server.Database/Migrations/Sqlite/20240215030858_Special.Designer.cs @@ -0,0 +1,1740 @@ +// +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("20240215030858_Special")] + partial class Special + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "7.0.4"); + + 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("Id") + .HasColumnType("INTEGER") + .HasColumnName("admin_log_id"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_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("Id", "RoundId") + .HasName("PK_admin_log"); + + b.HasIndex("Date"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_log_round_id"); + + b.HasIndex("Type") + .HasDatabaseName("IX_admin_log_type"); + + b.ToTable("admin_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLogEntity", b => + { + b.Property("Uid") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("uid"); + + b.Property("AdminLogId") + .HasColumnType("INTEGER") + .HasColumnName("admin_log_id"); + + b.Property("AdminLogRoundId") + .HasColumnType("INTEGER") + .HasColumnName("admin_log_round_id"); + + b.Property("Name") + .HasColumnType("TEXT") + .HasColumnName("name"); + + b.HasKey("Uid") + .HasName("PK_admin_log_entity"); + + b.HasIndex("AdminLogId", "AdminLogRoundId") + .HasDatabaseName("IX_admin_log_entity_admin_log_id_admin_log_round_id"); + + b.ToTable("admin_log_entity", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b => + { + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("LogId") + .HasColumnType("INTEGER") + .HasColumnName("log_id"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.HasKey("PlayerUserId", "LogId", "RoundId") + .HasName("PK_admin_log_player"); + + b.HasIndex("LogId", "RoundId"); + + 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("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", (string)null); + }); + + 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.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("HWId") + .HasColumnType("BLOB") + .HasColumnName("hwid"); + + 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("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.Special", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("special_id"); + + b.Property("SpecialName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("special_name"); + + b.Property("Priority") + .HasColumnType("INTEGER") + .HasColumnName("priority"); + + b.Property("ProfileId") + .HasColumnType("INTEGER") + .HasColumnName("profile_id"); + + b.HasKey("Id") + .HasName("PK_special"); + + b.HasIndex("ProfileId"); + + b.HasIndex("ProfileId", "SpecialName") + .IsUnique(); + + b.ToTable("special", (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("LastSeenHWId") + .HasColumnType("BLOB") + .HasColumnName("last_seen_hwid"); + + 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("Backpack") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("backpack"); + + b.Property("CharacterName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("char_name"); + + b.Property("Clothing") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("clothing"); + + 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("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.Round", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("ServerId") + .HasColumnType("INTEGER") + .HasColumnName("server_id"); + + b.HasKey("Id") + .HasName("PK_round"); + + b.HasIndex("ServerId") + .HasDatabaseName("IX_round_server_id"); + + 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("HWId") + .HasColumnType("BLOB") + .HasColumnName("hwid"); + + 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("HWId") + .HasColumnType("BLOB") + .HasColumnName("hwid"); + + 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.AdminLogEntity", b => + { + b.HasOne("Content.Server.Database.AdminLog", null) + .WithMany("Entities") + .HasForeignKey("AdminLogId", "AdminLogRoundId") + .HasConstraintName("FK_admin_log_entity_admin_log_admin_log_id_admin_log_round_id"); + }); + + 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("LogId", "RoundId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_player_admin_log_log_id_round_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.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.Special", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Specials") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_special_profile_profile_id"); + + b.Navigation("Profile"); + }); + + 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.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.Navigation("CreatedBy"); + + 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.Navigation("CreatedBy"); + + 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("Entities"); + + 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"); + }); + + 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("Traits"); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.Navigation("AdminLogs"); + }); + + modelBuilder.Entity("Content.Server.Database.Server", b => + { + 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/20240215030858_Special.cs b/Content.Server.Database/Migrations/Sqlite/20240215030858_Special.cs new file mode 100644 index 00000000000..77ad2d4e92d --- /dev/null +++ b/Content.Server.Database/Migrations/Sqlite/20240215030858_Special.cs @@ -0,0 +1,45 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Content.Server.Database.Migrations.Sqlite +{ + public partial class Special : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "special", + columns: table => new + { + special_id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + profile_id = table.Column(nullable: false), + special_name = table.Column(nullable: false), + priority = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_special", x => x.special_id); + table.ForeignKey( + name: "FK_special_profile_profile_id", + column: x => x.profile_id, + principalTable: "profile", + principalColumn: "profile_id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_special_profile_id", + table: "special", + column: "profile_id"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "special"); + } + } +} diff --git a/Content.Server.Database/Migrations/Sqlite/SqliteServerDbContextModelSnapshot.cs b/Content.Server.Database/Migrations/Sqlite/SqliteServerDbContextModelSnapshot.cs index e711247bc37..9d992a8e281 100644 --- a/Content.Server.Database/Migrations/Sqlite/SqliteServerDbContextModelSnapshot.cs +++ b/Content.Server.Database/Migrations/Sqlite/SqliteServerDbContextModelSnapshot.cs @@ -593,6 +593,38 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("loadout", (string)null); }); + + modelBuilder.Entity("Content.Server.Database.Special", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("special_id"); + + b.Property("SpecialName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("special_name"); + + b.Property("Priority") + .HasColumnType("INTEGER") + .HasColumnName("priority"); + + b.Property("ProfileId") + .HasColumnType("INTEGER") + .HasColumnName("profile_id"); + + b.HasKey("Id") + .HasName("PK_special"); + + b.HasIndex("ProfileId"); + + b.HasIndex("ProfileId", "SpecialName") + .IsUnique(); + + b.ToTable("special", (string)null); + }); + modelBuilder.Entity("Content.Server.Database.PlayTime", b => { b.Property("Id") @@ -1473,6 +1505,18 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Profile"); }); + modelBuilder.Entity("Content.Server.Database.Special", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Specials") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_special_profile_profile_id"); + + b.Navigation("Profile"); + }); + modelBuilder.Entity("Content.Server.Database.Loadout", b => { b.HasOne("Content.Server.Database.Profile", "Profile") diff --git a/Content.Server.Database/Model.cs b/Content.Server.Database/Model.cs index 6c6ae279df5..5319d5398c0 100644 --- a/Content.Server.Database/Model.cs +++ b/Content.Server.Database/Model.cs @@ -75,6 +75,15 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasIndex(j => new { j.ProfileId, j.JobName }) .IsUnique(); + // Nuclear14 Special + modelBuilder.Entity() + .HasIndex(j => j.ProfileId); + + modelBuilder.Entity() + .HasIndex(j => new { j.ProfileId, j.SpecialName }) + .IsUnique(); + // Nuclear14 end + modelBuilder.Entity() .HasIndex(p => p.UserName) .IsUnique(); @@ -347,6 +356,7 @@ public class Profile public string Backpack { get; set; } = null!; public int SpawnPriority { get; set; } = 0; public List Jobs { get; } = new(); + public List Specials { get; } = new(); // Nuclear14 Special public List Antags { get; } = new(); public List Traits { get; } = new(); public List Loadouts { get; } = new(); @@ -376,6 +386,33 @@ public enum DbJobPriority High = 3 } + // Nuclear14 Special + public class Special + { + public int Id { get; set; } + public Profile Profile { get; set; } = null!; + public int ProfileId { get; set; } + public string SpecialName { get; set; } = null!; + public DbSpecialPriority Priority { get; set; } + } + + public enum DbSpecialPriority + { + // These enum values HAVE to match the ones in SpecialPriority in Content.Shared + Zero = 0, + One = 1, + Two = 2, + Three = 3, + Four = 4, + Five = 5, + Six = 6, + Seven = 7, + Eight = 8, + Nine = 9, + Ten = 10 + } + // Nuclear14 end + public class Antag { public int Id { get; set; } diff --git a/Content.Server/Database/ServerDbBase.cs b/Content.Server/Database/ServerDbBase.cs index af9220ee6db..39a59e7c06a 100644 --- a/Content.Server/Database/ServerDbBase.cs +++ b/Content.Server/Database/ServerDbBase.cs @@ -17,6 +17,7 @@ using Robust.Shared.Enums; using Robust.Shared.Network; using Robust.Shared.Utility; +using Content.Shared.Nuclear14.Special; namespace Content.Server.Database { @@ -38,6 +39,7 @@ public ServerDbBase(ISawmill opsLog) var prefs = await db.DbContext .Preference .Include(p => p.Profiles).ThenInclude(h => h.Jobs) + .Include(p => p.Profiles).ThenInclude(h => h.Specials) // Nuclear14 Special .Include(p => p.Profiles).ThenInclude(h => h.Antags) .Include(p => p.Profiles).ThenInclude(h => h.Traits) .Include(p => p.Profiles).ThenInclude(h => h.Loadouts) @@ -87,6 +89,7 @@ public async Task SaveCharacterSlotAsync(NetUserId userId, ICharacterProfile? pr .Include(p => p.Preference) .Where(p => p.Preference.UserId == userId.UserId) .Include(p => p.Jobs) + .Include(p => p.Specials) // Nuclear14 Special .Include(p => p.Antags) .Include(p => p.Traits) .Include(p => p.Loadouts) @@ -174,6 +177,7 @@ private static async Task SetSelectedCharacterSlotAsync(NetUserId userId, int ne private static HumanoidCharacterProfile ConvertProfiles(Profile profile) { var jobs = profile.Jobs.ToDictionary(j => j.JobName, j => (JobPriority) j.Priority); + var specials = profile.Specials.ToDictionary(j => j.SpecialName, j => (SpecialPriority) j.Priority); // Nuclear14 Special var antags = profile.Antags.Select(a => a.AntagName); var traits = profile.Traits.Select(t => t.TraitName); var loadouts = profile.Loadouts.Select(t => t.LoadoutName); @@ -238,7 +242,8 @@ private static HumanoidCharacterProfile ConvertProfiles(Profile profile) (PreferenceUnavailableMode) profile.PreferenceUnavailable, antags.ToList(), traits.ToList(), - loadouts.ToList() + loadouts.ToList(), + specials // Nuclear14 Special ); } @@ -275,11 +280,19 @@ private static Profile ConvertProfiles(HumanoidCharacterProfile humanoid, int sl profile.PreferenceUnavailable = (DbPreferenceUnavailableMode) humanoid.PreferenceUnavailable; profile.Jobs.Clear(); + profile.Specials.Clear(); // Nuclear14 Special profile.Jobs.AddRange( humanoid.JobPriorities .Where(j => j.Value != JobPriority.Never) .Select(j => new Job {JobName = j.Key, Priority = (DbJobPriority) j.Value}) ); + // Nuclear14 Special + profile.Specials.AddRange( + humanoid.SpecialPriorities + .Where(j => j.Value != SpecialPriority.Zero) + .Select(j => new Special {SpecialName = j.Key, Priority = (DbSpecialPriority) j.Value}) + ); + // Nuclear14 end profile.Antags.Clear(); profile.Antags.AddRange( diff --git a/Content.Server/Medical/HealingSystem.cs b/Content.Server/Medical/HealingSystem.cs index e7362d481ed..c87ba21f4d3 100644 --- a/Content.Server/Medical/HealingSystem.cs +++ b/Content.Server/Medical/HealingSystem.cs @@ -16,6 +16,7 @@ using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; +using Content.Shared.Nuclear14.Special.Components; using Content.Shared.Stacks; using Robust.Shared.Audio.Systems; using Robust.Shared.Random; @@ -79,7 +80,52 @@ entity.Comp.DamageContainerID is not null && if (healing.ModifyBloodLevel != 0) _bloodstreamSystem.TryModifyBloodLevel(entity.Owner, healing.ModifyBloodLevel); - var healed = _damageable.TryChangeDamage(entity.Owner, healing.Damage, true, origin: args.Args.User); + // Nuclear14 Use Special for healing multiplier + // healing multiplier applying when getting healed by things like ointments + // It is different for doctor and patient depending on doctor Int, pacient Endurance / both Luck + // Multiply both of them to get final one. Below are single muiltipliers calculated for stats + // 1.13 when primal stat is 5 + // 1.46 when primal stat is 10 + // 0.86 when primal stat is 1 + // 1.23 when primal stat is 5, but luck 10 + // Example of final heal multiplier: + // 1.13 * 1.13 ~ 1.27 when both doctor and patient primal stat is 5 + // 1.13 * 1.46 ~ 1.64 when one primal stat is 10, another is 5 + // for each Primal point we get 0.067 = 6.7% + // for each Luck we get 0.02 = 2% + var newDic = healing.Damage; + foreach(var entry in newDic.DamageDict) + { + if (entry.Value >= 0) continue; + + float newValue = entry.Value.Float(); + if (TryComp(args.User, out var special)){ + newValue *= 0.70f + (special.TotalIntelligence / 15f + special.TotalLuck / 50f); + } + if (TryComp(entity.Owner, out var special2)){ + newValue *= 0.70f + (special2.TotalEndurance / 15f + special2.TotalLuck / 50f); + } + newDic.DamageDict[entry.Key] = newValue; + } + + var healed = _damageable.TryChangeDamage(entity.Owner, newDic, true, origin: args.Args.User); + + // remove modifier after perfoming healing, to prevent accumulating + foreach(var entry in newDic.DamageDict) + { + if (entry.Value >= 0) continue; + + float newValue = entry.Value.Float(); + // todo: use log + if (TryComp(args.User, out var special)){ + newValue /= 0.70f + (special.TotalIntelligence / 15f + special.TotalLuck / 50f); + } + if (TryComp(entity.Owner, out var special2)){ + newValue /= 0.70f + (special2.TotalEndurance / 15f + special2.TotalLuck / 50f); + } + newDic.DamageDict[entry.Key] = newValue; + } + //Nuclear14 end if (healed == null && healing.BloodlossModifier != 0) return; diff --git a/Content.Server/Nuclear14/Special/EntitySystems/SpecialSystem.cs b/Content.Server/Nuclear14/Special/EntitySystems/SpecialSystem.cs new file mode 100644 index 00000000000..e2f7a3a409b --- /dev/null +++ b/Content.Server/Nuclear14/Special/EntitySystems/SpecialSystem.cs @@ -0,0 +1,100 @@ +using Content.Server.GameTicking; +using Content.Server.Nuclear14.Special.Speech.Components; +using Content.Shared.Hands.Components; +using Content.Shared.Hands.EntitySystems; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.Manager; +using Content.Shared.Nuclear14.Special.Components; +using Content.Shared.Nuclear14.Special; +using Content.Shared.Nuclear14.Special.Components; +using Content.Server.NPC.Systems; + +namespace Content.Server.Nuclear.Special.EntitySystems; + +public sealed class SpecialSystem : EntitySystem +{ + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnPlayerSpawnComplete); + + SubscribeLocalEvent(OnSpecialModifiersChanged); + + } + + // When the player is spawned in, add all trait components selected during character creation + private void OnPlayerSpawnComplete(EntityUid uid, SpecialComponent component, PlayerSpawnCompleteEvent args) + { + if (!EntityManager.TryGetComponent(uid, out var special)) + { + return; + } + + foreach (var item in args.Profile.SpecialPriorities) + { + if (!_prototypeManager.TryIndex(item.Key, out var specialPrototype)) + { + Logger.Warning($"No special prototype found with ID {item.Key}!"); + return; + } + setSpecial(special, specialPrototype, item.Value); + } + + if (special.TotalIntelligence < 3) + { + EntityManager.AddComponent(uid); + } + else + { + EntityManager.RemoveComponent(uid); + } + } + + private void setSpecial(SpecialComponent component, + SpecialPrototype prototype, + SpecialPriority priority) + { + switch(prototype.ID) + { + case "Strength": + component.BaseStrength = (int) priority; + return; + case "Perception": + component.BasePerception = (int) priority; + return; + case "Endurance": + component.BaseEndurance = (int) priority; + return; + case "Charisma": + component.BaseCharisma = (int) priority; + return; + case "Intelligence": + component.BaseIntelligence = (int) priority; + return; + case "Agility": + component.BaseAgility = (int) priority; + return; + case "Luck": + component.BaseLuck = (int) priority; + return; + default: + return; + } + } + + private void OnSpecialModifiersChanged(EntityUid uid, SpecialComponent component, RefreshSpecialModifiersDoAfterEvent args) + { + if (component.TotalIntelligence < 3) + { + EnsureComp(uid); + } + else + { + EntityManager.RemoveComponent(uid); + } + + } +} diff --git a/Content.Server/Nuclear14/Special/Speech/Components/LowIntelligenceAccentComponent.cs b/Content.Server/Nuclear14/Special/Speech/Components/LowIntelligenceAccentComponent.cs new file mode 100644 index 00000000000..15dd3fd9939 --- /dev/null +++ b/Content.Server/Nuclear14/Special/Speech/Components/LowIntelligenceAccentComponent.cs @@ -0,0 +1,4 @@ +namespace Content.Server.Nuclear14.Special.Speech.Components; + +[RegisterComponent] +public sealed partial class LowIntelligenceAccentComponent : Component {} diff --git a/Content.Server/Nuclear14/Special/Speech/Systems/LowIntelligenceAccentSystem.cs b/Content.Server/Nuclear14/Special/Speech/Systems/LowIntelligenceAccentSystem.cs new file mode 100644 index 00000000000..6442691c8e0 --- /dev/null +++ b/Content.Server/Nuclear14/Special/Speech/Systems/LowIntelligenceAccentSystem.cs @@ -0,0 +1,45 @@ +using System.Text.RegularExpressions; +using Content.Server.Speech; +using Content.Server.Speech.Components; +using Content.Server.Nuclear14.Special.Speech.Components; +using Robust.Shared.Random; + +namespace Content.Server.Nuclear14.Special.Speech.EntitySystems +{ + public sealed class LowIntelligenceAccentSystem : EntitySystem + { + [Dependency] private readonly IRobustRandom _random = default!; + + private static readonly Regex yesRegex = new(@"(?:yes\b|yep\b)", RegexOptions.Compiled | RegexOptions.IgnoreCase); + private static readonly Regex noRegex = new(@"(?:no\b|nope\b)", RegexOptions.Compiled | RegexOptions.IgnoreCase); + private static readonly Regex ingRegex = new(@"(?:ing\b|in\b)", RegexOptions.Compiled | RegexOptions.IgnoreCase); + private static readonly Regex ilityRegex = new(@"(?:ility\b)", RegexOptions.Compiled | RegexOptions.IgnoreCase); + private static readonly Regex tionRegex = new(@"(?:tion\b|tions\b)", RegexOptions.Compiled | RegexOptions.IgnoreCase); + private static readonly Regex lettersRegex = new(@"[ao]", RegexOptions.Compiled | RegexOptions.IgnoreCase); + + public override void Initialize() + { + SubscribeLocalEvent(OnAccent); + } + + public string Accentuate(string message) + { + message = message.Trim(); + + message = yesRegex.Replace(message, "duh"); + message = noRegex.Replace(message, "nungh"); + message = ingRegex.Replace(message, "uuuh"); + message = ilityRegex.Replace(message, "uuity"); + message = tionRegex.Replace(message, "tuun"); + + message = lettersRegex.Replace(message, "u"); + + return message; + } + + private void OnAccent(EntityUid uid, LowIntelligenceAccentComponent component, AccentGetEvent args) + { + args.Message = Accentuate(args.Message); + } + } +} diff --git a/Content.Server/Preferences/Managers/ServerPreferencesManager.cs b/Content.Server/Preferences/Managers/ServerPreferencesManager.cs index e262fde64d2..9a89915637f 100644 --- a/Content.Server/Preferences/Managers/ServerPreferencesManager.cs +++ b/Content.Server/Preferences/Managers/ServerPreferencesManager.cs @@ -13,6 +13,7 @@ using Robust.Shared.Network; using Robust.Shared.Player; using Robust.Shared.Prototypes; +using Content.Shared.Nuclear14.Special; namespace Content.Server.Preferences.Managers diff --git a/Content.Server/Projectiles/ProjectileSystem.cs b/Content.Server/Projectiles/ProjectileSystem.cs index 0061b16e47c..e678f8d78c0 100644 --- a/Content.Server/Projectiles/ProjectileSystem.cs +++ b/Content.Server/Projectiles/ProjectileSystem.cs @@ -7,6 +7,10 @@ using Content.Shared.Projectiles; using Robust.Shared.Physics.Events; using Robust.Shared.Player; +using Content.Shared.Effects; +using Content.Shared.Nuclear14.Special.Components; +using Content.Shared.Popups; +using Robust.Shared.Random; namespace Content.Server.Projectiles; @@ -17,6 +21,8 @@ public sealed class ProjectileSystem : SharedProjectileSystem [Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private readonly GunSystem _guns = default!; [Dependency] private readonly SharedCameraRecoilSystem _sharedCameraRecoil = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly IRobustRandom _random = default!; public override void Initialize() { @@ -46,6 +52,20 @@ private void OnStartCollide(EntityUid uid, ProjectileComponent component, ref St var otherName = ToPrettyString(target); var direction = args.OurBody.LinearVelocity.Normalized(); + + // Nuclear14 Avoid geing hit with projectiles! pure luck? + if (TryComp(target, out var special)) + { + var rand = _random.Next(100); + if (special.TotalLuck > rand) + { + var message = Loc.GetString("special-lucky-evasion"); + _popup.PopupEntity(message, target); + return; + } + } + // Nuclear14 end + var modifiedDamage = _damageableSystem.TryChangeDamage(target, ev.Damage, component.IgnoreResistances, origin: component.Shooter); var deleted = Deleted(target); diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs index bf60208e958..2a070630d1d 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs @@ -24,6 +24,8 @@ using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Utility; +using Content.Shared.Nuclear14.Special.Components; +using Content.Shared.Popups; namespace Content.Server.Weapons.Ranged.Systems; @@ -321,7 +323,23 @@ private Angle[] LinearSpread(Angle start, Angle end, int intervals) private Angle GetRecoilAngle(TimeSpan curTime, GunComponent component, Angle direction, EntityUid? shooter) { var timeSinceLastFire = (curTime - component.LastFire).TotalSeconds; - var newTheta = MathHelper.Clamp(component.CurrentAngle.Theta + component.AngleIncreaseModified.Theta - component.AngleDecayModified.Theta * timeSinceLastFire, component.MinAngleModified.Theta, component.MaxAngleModified.Theta); + var minAngle = component.MinAngleModified; + var decay = component.AngleDecayModified; + var maxAngle = component.MaxAngleModified; + Angle newTheta; + if (TryComp(shooter, out var special)) + { + maxAngle += Angle.FromDegrees((40f - special.TotalPerception * 5)); + minAngle += Angle.FromDegrees((10f - special.TotalPerception)); + maxAngle *= 1.5 - special.TotalPerception / 10; + minAngle *= 1.5 - special.TotalPerception / 10; + decay += Angle.FromDegrees((special.TotalPerception - 10f) / 5); + + newTheta = MathHelper.Clamp(component.CurrentAngle.Theta + component.AngleIncreaseModified.Theta - decay.Theta * timeSinceLastFire, minAngle.Theta, maxAngle.Theta); + } + else + newTheta = MathHelper.Clamp(component.CurrentAngle.Theta + component.AngleIncreaseModified.Theta - component.AngleDecayModified.Theta * timeSinceLastFire, component.MinAngleModified.Theta, component.MaxAngleModified.Theta); + // Nuclear14 end component.CurrentAngle = new Angle(newTheta); component.LastFire = component.NextFire; diff --git a/Content.Shared/Inventory/InventorySystem.Equip.cs b/Content.Shared/Inventory/InventorySystem.Equip.cs index 7bdd17ee6fa..78321d8f82d 100644 --- a/Content.Shared/Inventory/InventorySystem.Equip.cs +++ b/Content.Shared/Inventory/InventorySystem.Equip.cs @@ -14,6 +14,7 @@ using Robust.Shared.Containers; using Robust.Shared.Timing; using Robust.Shared.Utility; +using Content.Shared.Nuclear14.Special; namespace Content.Shared.Inventory; @@ -21,6 +22,9 @@ public abstract partial class InventorySystem { [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly MovementSpeedModifierSystem _movementSpeed = default!; + // Nuclear14 Cloth Special Modifiers + [Dependency] private readonly SpecialModifierSystem _specialModifiers = default!; + // Nuclear14 end [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] private readonly SharedItemSystem _item = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; @@ -195,6 +199,10 @@ public bool TryEquip(EntityUid actor, EntityUid target, EntityUid itemUid, strin _movementSpeed.RefreshMovementSpeedModifiers(target); + // Nuclear14 Cloth Special Modifiers + _specialModifiers.RefreshClothingSpecialModifiers(target); + // Nuclear14 end + return true; } @@ -451,6 +459,10 @@ public bool TryUnequip( _movementSpeed.RefreshMovementSpeedModifiers(target); + // Nuclear14 Cloth Special Modifiers + _specialModifiers.RefreshClothingSpecialModifiers(target); + // Nuclear14 end + return true; } diff --git a/Content.Shared/Inventory/InventorySystem.Relay.cs b/Content.Shared/Inventory/InventorySystem.Relay.cs index c43a5885077..dd3b701c8e6 100644 --- a/Content.Shared/Inventory/InventorySystem.Relay.cs +++ b/Content.Shared/Inventory/InventorySystem.Relay.cs @@ -12,6 +12,8 @@ using Content.Shared.Strip.Components; using Content.Shared.Temperature; using Content.Shared.Verbs; +using Robust.Shared.Containers; +using Content.Shared.Nuclear14.Special; namespace Content.Shared.Inventory; @@ -46,6 +48,9 @@ public void InitializeRelay() SubscribeLocalEvent>(RelayInventoryEvent); SubscribeLocalEvent>(OnGetEquipmentVerbs); + // Nuclear14 Cloth Special Modifiers + SubscribeLocalEvent(RelayInventoryEvent); + // Nuclear14 end } protected void RefRelayInventoryEvent(EntityUid uid, InventoryComponent component, ref T args) where T : IInventoryRelayEvent diff --git a/Content.Shared/Mobs/Components/MobThresholdsComponent.cs b/Content.Shared/Mobs/Components/MobThresholdsComponent.cs index e97d3672a21..51958004f0d 100644 --- a/Content.Shared/Mobs/Components/MobThresholdsComponent.cs +++ b/Content.Shared/Mobs/Components/MobThresholdsComponent.cs @@ -42,6 +42,13 @@ public sealed partial class MobThresholdsComponent : Component /// [DataField("allowRevives")] public bool AllowRevives; + + /// + /// Track initial trashholds to modify thresholds in runtime depending on curent special value. + /// + [DataField("initialThresholds")] + public SortedDictionary BaseThresholds = new(); + } [Serializable, NetSerializable] diff --git a/Content.Shared/Mobs/Systems/MobThresholdSystem.cs b/Content.Shared/Mobs/Systems/MobThresholdSystem.cs index 59d9fb4c239..ee72e8ee6db 100644 --- a/Content.Shared/Mobs/Systems/MobThresholdSystem.cs +++ b/Content.Shared/Mobs/Systems/MobThresholdSystem.cs @@ -5,6 +5,7 @@ using Content.Shared.FixedPoint; using Content.Shared.Mobs.Components; using Robust.Shared.GameStates; +using Content.Shared.Nuclear14.Special.Components; namespace Content.Shared.Mobs.Systems; @@ -27,10 +28,14 @@ public override void Initialize() private void OnGetState(EntityUid uid, MobThresholdsComponent component, ref ComponentGetState args) { + // Nuclear14 HP depends on Endurance + var totalEndurance = 0; + if (TryComp(uid, out var special)) + totalEndurance = special.TotalEndurance; var thresholds = new Dictionary(); - foreach (var (key, value) in component.Thresholds) + foreach (var (key, value) in component.BaseThresholds) { - thresholds.Add(key, value); + thresholds.Add(key + FixedPoint2.New(totalEndurance * 5), value); } args.State = new MobThresholdsComponentState(thresholds, component.TriggersAlerts, @@ -38,13 +43,26 @@ private void OnGetState(EntityUid uid, MobThresholdsComponent component, ref Com component.StateAlertDict, component.ShowOverlays, component.AllowRevives); + // Nuclear14 end } private void OnHandleState(EntityUid uid, MobThresholdsComponent component, ref ComponentHandleState args) { + // Nuclear14 HP depends on Endurance + var totalEndurance = 0; + if (TryComp(uid, out var special)) + totalEndurance = special.TotalEndurance; + var thresholds = new Dictionary(); + foreach (var (key, value) in component.BaseThresholds) + { + thresholds.Add(key + FixedPoint2.New(totalEndurance * 5), value); + } + if (args.Current is not MobThresholdsComponentState state) return; - component.Thresholds = new SortedDictionary(state.UnsortedThresholds); + component.Thresholds = new SortedDictionary(thresholds); + // component.Thresholds = new SortedDictionary(state.UnsortedThresholds); + // Nuclear14 end component.TriggersAlerts = state.TriggersAlerts; component.CurrentThresholdState = state.CurrentThresholdState; component.AllowRevives = state.AllowRevives; @@ -96,12 +114,15 @@ public FixedPoint2 GetThresholdForState(EntityUid target, MobState mobState, { if (!Resolve(target, ref thresholdComponent)) return FixedPoint2.Zero; + var totalEndurance = 0; + if (TryComp(target, out var special)) + totalEndurance = special.TotalEndurance; foreach (var pair in thresholdComponent.Thresholds) { if (pair.Value == mobState) { - return pair.Key; + return pair.Key + FixedPoint2.New(totalEndurance * 5); } } @@ -120,6 +141,10 @@ public bool TryGetThresholdForState(EntityUid target, MobState mobState, [NotNullWhen(true)] out FixedPoint2? threshold, MobThresholdsComponent? thresholdComponent = null) { + var totalEndurance = 0; + if (TryComp(target, out var special)) + totalEndurance = special.TotalEndurance; + threshold = null; if (!Resolve(target, ref thresholdComponent)) return false; @@ -128,7 +153,7 @@ public bool TryGetThresholdForState(EntityUid target, MobState mobState, { if (pair.Value == mobState) { - threshold = pair.Key; + threshold = pair.Key + FixedPoint2.New(totalEndurance * 5); return true; } } @@ -196,8 +221,11 @@ public bool TryGetIncapPercentage(EntityUid target, FixedPoint2 damage, percentage = 0; return true; } + var totalEndurance = 0; + if (TryComp(target, out var special)) + totalEndurance = special.TotalEndurance; - percentage = FixedPoint2.Min(1.0f, damage / threshold.Value); + percentage = FixedPoint2.Min(1.0f, damage / (threshold.Value + FixedPoint2.New(totalEndurance * 5))); return true; } @@ -283,6 +311,9 @@ public void SetMobStateThreshold(EntityUid target, FixedPoint2 damage, MobState { if (!Resolve(target, ref threshold)) return; + var totalEndurance = 0; + if (TryComp(target, out var special)) + totalEndurance = special.TotalEndurance; // create a duplicate dictionary so we don't modify while enumerating. var thresholds = new Dictionary(threshold.Thresholds); @@ -292,7 +323,7 @@ public void SetMobStateThreshold(EntityUid target, FixedPoint2 damage, MobState continue; threshold.Thresholds.Remove(damageThreshold); } - threshold.Thresholds[damage] = mobState; + threshold.Thresholds[damage + FixedPoint2.New(totalEndurance * 5)] = mobState; Dirty(target, threshold); VerifyThresholds(target, threshold); } @@ -334,9 +365,13 @@ public void SetAllowRevives(EntityUid uid, bool val, MobThresholdsComponent? com private void CheckThresholds(EntityUid target, MobStateComponent mobStateComponent, MobThresholdsComponent thresholdsComponent, DamageableComponent damageableComponent, EntityUid? origin = null) { + var totalEndurance = 0; + if (TryComp(target, out var special)) + totalEndurance = special.TotalEndurance; + foreach (var (threshold, mobState) in thresholdsComponent.Thresholds.Reverse()) { - if (damageableComponent.TotalDamage < threshold) + if (damageableComponent.TotalDamage < threshold + FixedPoint2.New(totalEndurance * 5)) continue; TriggerThreshold(target, mobState, mobStateComponent, thresholdsComponent, origin); @@ -424,6 +459,9 @@ private void MobThresholdStartup(EntityUid target, MobThresholdsComponent thresh { if (!TryComp(target, out var mobState) || !TryComp(target, out var damageable)) return; + + thresholds.BaseThresholds = new SortedDictionary(thresholds.Thresholds); // Nuclear14 HP depends on Endurance + CheckThresholds(target, mobState, thresholds, damageable); UpdateAllEffects((target, thresholds, mobState, damageable), mobState.CurrentState); } diff --git a/Content.Shared/Nuclear14/CCVar/SpecialCCVars.cs b/Content.Shared/Nuclear14/CCVar/SpecialCCVars.cs new file mode 100644 index 00000000000..048eed971d0 --- /dev/null +++ b/Content.Shared/Nuclear14/CCVar/SpecialCCVars.cs @@ -0,0 +1,44 @@ +using Robust.Shared.Configuration; + +namespace Content.Shared.Nuclear14.CCVar; + +[CVarDefs] +public sealed class SpecialCCVars +{ + + #region Human editor + + public static readonly CVarDef MaxSpecial = + CVarDef.Create("special.max", 40); + + #endregion + + #region Strength + + /// + /// Strength to be able to weild anything + /// + public static readonly CVarDef StrengthWeild = + CVarDef.Create("specialStrength.weild", 3); + + #endregion + + #region Perception + #endregion + + #region Endurance + #endregion + + #region Charisma + #endregion + + #region Intelligence + #endregion + + #region Agility + #endregion + + #region Luck + #endregion + +} \ No newline at end of file diff --git a/Content.Shared/Nuclear14/Special/ChemistryModifiers/Agility/AgilityModifier.cs b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Agility/AgilityModifier.cs new file mode 100644 index 00000000000..e2948505d65 --- /dev/null +++ b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Agility/AgilityModifier.cs @@ -0,0 +1,65 @@ +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.Movement.Systems; +using Robust.Shared.Prototypes; +using Robust.Shared.Timing; +using Content.Shared.Nuclear14.Special; + +namespace Content.Shared.Nuclear14.Special.ChemistryModifiers; + + /// + /// Default metabolism for stimulants and tranqs. Attempts to find a AgilityModifier on the target, + /// adding one if not there and to change the Agility + /// +public sealed partial class AgilityModifierReagent : ReagentEffect +{ + + [DataField("agilityModifier")] + public int AgilityModifier { get; set; } = 0; + + /// + /// How long the modifier applies (in seconds) when metabolized. + /// + [DataField("statusLifetime")] + public float StatusLifetime = 2f; + + protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) + { + return Loc.GetString("reagent-effect-guidebook-agility-modifier", + ("chance", Probability), + ("agility", AgilityModifier), + ("time", StatusLifetime)); + } + + /// + /// Remove reagent at set rate, changes the Agility modifier and adds a AgilityModifierMetabolismComponent if not already there. + /// + public override void Effect(ReagentEffectArgs args) + { + var status = args.EntityManager.EnsureComponent(args.SolutionEntity); + + // Only refresh movement if we need to. + var modified = !status.AgilityModifier.Equals(AgilityModifier); + + status.AgilityModifier = AgilityModifier; + + // only going to scale application time + var statusLifetime = StatusLifetime; + statusLifetime *= args.Scale; + + IncreaseTimer(status, statusLifetime); + + if (modified) + EntitySystem.Get().RefreshClothingSpecialModifiers(args.SolutionEntity); + + } + public void IncreaseTimer(AgilityModifierMetabolismComponent status, float time) + { + var gameTiming = IoCManager.Resolve(); + + var offsetTime = Math.Max(status.ModifierTimer.TotalSeconds, gameTiming.CurTime.TotalSeconds); + + status.ModifierTimer = TimeSpan.FromSeconds(offsetTime + time); + status.Dirty(); + } +} diff --git a/Content.Shared/Nuclear14/Special/ChemistryModifiers/Agility/AgilityModifierMetabolismComponent.cs b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Agility/AgilityModifierMetabolismComponent.cs new file mode 100644 index 00000000000..7495855bb6c --- /dev/null +++ b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Agility/AgilityModifierMetabolismComponent.cs @@ -0,0 +1,33 @@ +using Robust.Shared.Serialization; +using Robust.Shared.GameStates; + +namespace Content.Shared.Nuclear14.Special.ChemistryModifiers; + +[RegisterComponent] +[NetworkedComponent] +public sealed partial class AgilityModifierMetabolismComponent : Component +{ + [ViewVariables] + public int AgilityModifier { get; set; } + + /// + /// When the current modifier is expected to end. + /// + [ViewVariables] + public TimeSpan ModifierTimer { get; set; } = TimeSpan.Zero; + + [Serializable, NetSerializable] + public sealed class AgilityModifierMetabolismComponentState : ComponentState + { + public int AgilityModifier { get; } + public TimeSpan ModifierTimer { get; } + + public AgilityModifierMetabolismComponentState(int agilityModifier, TimeSpan modifierTimer) + { + AgilityModifier = agilityModifier; + ModifierTimer = modifierTimer; + } + } +} + + diff --git a/Content.Shared/Nuclear14/Special/ChemistryModifiers/Agility/MetabolismAgilityModifierSystem.cs b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Agility/MetabolismAgilityModifierSystem.cs new file mode 100644 index 00000000000..8de7ae3ec0c --- /dev/null +++ b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Agility/MetabolismAgilityModifierSystem.cs @@ -0,0 +1,78 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Timing; +using Content.Shared.Movement.Components; +using Content.Shared.Movement.Systems; +using static Content.Shared.Nuclear14.Special.ChemistryModifiers.AgilityModifierMetabolismComponent; +using Content.Shared.Nuclear14.Special.ChemistryModifiers; + +namespace Content.Shared.Nuclear14.Special.ChemistryModifiers; +public sealed partial class MetabolismAgilityModifierSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly SpecialModifierSystem _specialModifiers = default!; + + private readonly List _components = new(); + + public override void Initialize() + { + base.Initialize(); + + UpdatesOutsidePrediction = true; + + SubscribeLocalEvent(OnMovespeedHandleState); + SubscribeLocalEvent(OnGetState); + SubscribeLocalEvent(AddComponent); + SubscribeLocalEvent(OnRefreshMovespeed); + } + + private void OnGetState(EntityUid uid, AgilityModifierMetabolismComponent component, ref ComponentGetState args) + { + args.State = new AgilityModifierMetabolismComponentState( + component.AgilityModifier, + component.ModifierTimer); + } + + private void OnMovespeedHandleState(EntityUid uid, AgilityModifierMetabolismComponent component, ref ComponentHandleState args) + { + if (args.Current is not AgilityModifierMetabolismComponentState cast) + return; + + component.AgilityModifier = cast.AgilityModifier; + component.ModifierTimer = cast.ModifierTimer; + } + + private void OnRefreshMovespeed(EntityUid uid, AgilityModifierMetabolismComponent component, RefreshSpecialModifiersEvent args) + { + args.ModifyAgility(component.AgilityModifier); + } + + private void AddComponent(EntityUid uid, AgilityModifierMetabolismComponent component, ComponentStartup args) + { + _components.Add(component); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var currentTime = _gameTiming.CurTime; + + for (var i = _components.Count - 1; i >= 0; i--) + { + var component = _components[i]; + + if (component.Deleted) + { + _components.RemoveAt(i); + continue; + } + + if (component.ModifierTimer > currentTime) continue; + + _components.RemoveAt(i); + EntityManager.RemoveComponent(component.Owner); + + _specialModifiers.RefreshClothingSpecialModifiers(component.Owner); + } + } +} diff --git a/Content.Shared/Nuclear14/Special/ChemistryModifiers/Charisma/CharismaModifier.cs b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Charisma/CharismaModifier.cs new file mode 100644 index 00000000000..4d2821797dc --- /dev/null +++ b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Charisma/CharismaModifier.cs @@ -0,0 +1,65 @@ +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.Movement.Systems; +using Robust.Shared.Prototypes; +using Robust.Shared.Timing; +using Content.Shared.Nuclear14.Special; + +namespace Content.Shared.Nuclear14.Special.ChemistryModifiers; + + /// + /// Default metabolism for stimulants and tranqs. Attempts to find a CharismaModifier on the target, + /// adding one if not there and to change the Charisma + /// +public sealed partial class CharismaModifierReagent : ReagentEffect +{ + + [DataField("charismaModifier")] + public int CharismaModifier { get; set; } = 0; + + /// + /// How long the modifier applies (in seconds) when metabolized. + /// + [DataField("statusLifetime")] + public float StatusLifetime = 2f; + + protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) + { + return Loc.GetString("reagent-effect-guidebook-charisma-modifier", + ("chance", Probability), + ("charisma", CharismaModifier), + ("time", StatusLifetime)); + } + + /// + /// Remove reagent at set rate, changes the Charisma modifier and adds a CharismaModifierMetabolismComponent if not already there. + /// + public override void Effect(ReagentEffectArgs args) + { + var status = args.EntityManager.EnsureComponent(args.SolutionEntity); + + // Only refresh movement if we need to. + var modified = !status.CharismaModifier.Equals(CharismaModifier); + + status.CharismaModifier = CharismaModifier; + + // only going to scale application time + var statusLifetime = StatusLifetime; + statusLifetime *= args.Scale; + + IncreaseTimer(status, statusLifetime); + + if (modified) + EntitySystem.Get().RefreshClothingSpecialModifiers(args.SolutionEntity); + + } + public void IncreaseTimer(CharismaModifierMetabolismComponent status, float time) + { + var gameTiming = IoCManager.Resolve(); + + var offsetTime = Math.Max(status.ModifierTimer.TotalSeconds, gameTiming.CurTime.TotalSeconds); + + status.ModifierTimer = TimeSpan.FromSeconds(offsetTime + time); + status.Dirty(); + } +} diff --git a/Content.Shared/Nuclear14/Special/ChemistryModifiers/Charisma/CharismaModifierMetabolismComponent.cs b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Charisma/CharismaModifierMetabolismComponent.cs new file mode 100644 index 00000000000..8e8abba5840 --- /dev/null +++ b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Charisma/CharismaModifierMetabolismComponent.cs @@ -0,0 +1,33 @@ +using Robust.Shared.Serialization; +using Robust.Shared.GameStates; + +namespace Content.Shared.Nuclear14.Special.ChemistryModifiers; + +[RegisterComponent] +[NetworkedComponent] +public sealed partial class CharismaModifierMetabolismComponent : Component +{ + [ViewVariables] + public int CharismaModifier { get; set; } + + /// + /// When the current modifier is expected to end. + /// + [ViewVariables] + public TimeSpan ModifierTimer { get; set; } = TimeSpan.Zero; + + [Serializable, NetSerializable] + public sealed class CharismaModifierMetabolismComponentState : ComponentState + { + public int CharismaModifier { get; } + public TimeSpan ModifierTimer { get; } + + public CharismaModifierMetabolismComponentState(int charismaModifier, TimeSpan modifierTimer) + { + CharismaModifier = charismaModifier; + ModifierTimer = modifierTimer; + } + } +} + + diff --git a/Content.Shared/Nuclear14/Special/ChemistryModifiers/Charisma/MetabolismCharismaModifierSystem.cs b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Charisma/MetabolismCharismaModifierSystem.cs new file mode 100644 index 00000000000..490b1edc923 --- /dev/null +++ b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Charisma/MetabolismCharismaModifierSystem.cs @@ -0,0 +1,78 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Timing; +using Content.Shared.Movement.Components; +using Content.Shared.Movement.Systems; +using static Content.Shared.Nuclear14.Special.ChemistryModifiers.CharismaModifierMetabolismComponent; +using Content.Shared.Nuclear14.Special.ChemistryModifiers; + +namespace Content.Shared.Nuclear14.Special.ChemistryModifiers; +public sealed partial class MetabolismCharismaModifierSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly SpecialModifierSystem _specialModifiers = default!; + + private readonly List _components = new(); + + public override void Initialize() + { + base.Initialize(); + + UpdatesOutsidePrediction = true; + + SubscribeLocalEvent(OnMovespeedHandleState); + SubscribeLocalEvent(OnGetState); + SubscribeLocalEvent(AddComponent); + SubscribeLocalEvent(OnRefreshMovespeed); + } + + private void OnGetState(EntityUid uid, CharismaModifierMetabolismComponent component, ref ComponentGetState args) + { + args.State = new CharismaModifierMetabolismComponentState( + component.CharismaModifier, + component.ModifierTimer); + } + + private void OnMovespeedHandleState(EntityUid uid, CharismaModifierMetabolismComponent component, ref ComponentHandleState args) + { + if (args.Current is not CharismaModifierMetabolismComponentState cast) + return; + + component.CharismaModifier = cast.CharismaModifier; + component.ModifierTimer = cast.ModifierTimer; + } + + private void OnRefreshMovespeed(EntityUid uid, CharismaModifierMetabolismComponent component, RefreshSpecialModifiersEvent args) + { + args.ModifyCharisma(component.CharismaModifier); + } + + private void AddComponent(EntityUid uid, CharismaModifierMetabolismComponent component, ComponentStartup args) + { + _components.Add(component); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var currentTime = _gameTiming.CurTime; + + for (var i = _components.Count - 1; i >= 0; i--) + { + var component = _components[i]; + + if (component.Deleted) + { + _components.RemoveAt(i); + continue; + } + + if (component.ModifierTimer > currentTime) continue; + + _components.RemoveAt(i); + EntityManager.RemoveComponent(component.Owner); + + _specialModifiers.RefreshClothingSpecialModifiers(component.Owner); + } + } +} diff --git a/Content.Shared/Nuclear14/Special/ChemistryModifiers/Endurance/EnduranceModifier.cs b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Endurance/EnduranceModifier.cs new file mode 100644 index 00000000000..de55e825049 --- /dev/null +++ b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Endurance/EnduranceModifier.cs @@ -0,0 +1,65 @@ +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.Movement.Systems; +using Robust.Shared.Prototypes; +using Robust.Shared.Timing; +using Content.Shared.Nuclear14.Special; + +namespace Content.Shared.Nuclear14.Special.ChemistryModifiers; + + /// + /// Default metabolism for stimulants and tranqs. Attempts to find a EnduranceModifier on the target, + /// adding one if not there and to change the endurance + /// +public sealed partial class EnduranceModifierReagent : ReagentEffect +{ + + [DataField("enduranceModifier")] + public int EnduranceModifier { get; set; } = 0; + + /// + /// How long the modifier applies (in seconds) when metabolized. + /// + [DataField("statusLifetime")] + public float StatusLifetime = 2f; + + protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) + { + return Loc.GetString("reagent-effect-guidebook-endurance-modifier", + ("chance", Probability), + ("endurance", EnduranceModifier), + ("time", StatusLifetime)); + } + + /// + /// Remove reagent at set rate, changes the endurance modifier and adds a EnduranceModifierMetabolismComponent if not already there. + /// + public override void Effect(ReagentEffectArgs args) + { + var status = args.EntityManager.EnsureComponent(args.SolutionEntity); + + // Only refresh movement if we need to. + var modified = !status.EnduranceModifier.Equals(EnduranceModifier); + + status.EnduranceModifier = EnduranceModifier; + + // only going to scale application time + var statusLifetime = StatusLifetime; + statusLifetime *= args.Scale; + + IncreaseTimer(status, statusLifetime); + + if (modified) + EntitySystem.Get().RefreshClothingSpecialModifiers(args.SolutionEntity); + + } + public void IncreaseTimer(EnduranceModifierMetabolismComponent status, float time) + { + var gameTiming = IoCManager.Resolve(); + + var offsetTime = Math.Max(status.ModifierTimer.TotalSeconds, gameTiming.CurTime.TotalSeconds); + + status.ModifierTimer = TimeSpan.FromSeconds(offsetTime + time); + status.Dirty(); + } +} diff --git a/Content.Shared/Nuclear14/Special/ChemistryModifiers/Endurance/EnduranceModifierMetabolismComponent.cs b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Endurance/EnduranceModifierMetabolismComponent.cs new file mode 100644 index 00000000000..b8008b445a8 --- /dev/null +++ b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Endurance/EnduranceModifierMetabolismComponent.cs @@ -0,0 +1,33 @@ +using Robust.Shared.Serialization; +using Robust.Shared.GameStates; + +namespace Content.Shared.Nuclear14.Special.ChemistryModifiers; + +[RegisterComponent] +[NetworkedComponent] +public sealed partial class EnduranceModifierMetabolismComponent : Component +{ + [ViewVariables] + public int EnduranceModifier { get; set; } + + /// + /// When the current modifier is expected to end. + /// + [ViewVariables] + public TimeSpan ModifierTimer { get; set; } = TimeSpan.Zero; + + [Serializable, NetSerializable] + public sealed class EnduranceModifierMetabolismComponentState : ComponentState + { + public int EnduranceModifier { get; } + public TimeSpan ModifierTimer { get; } + + public EnduranceModifierMetabolismComponentState(int enduranceModifier, TimeSpan modifierTimer) + { + EnduranceModifier = enduranceModifier; + ModifierTimer = modifierTimer; + } + } +} + + diff --git a/Content.Shared/Nuclear14/Special/ChemistryModifiers/Endurance/MetabolismEnduranceModifierSystem.cs b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Endurance/MetabolismEnduranceModifierSystem.cs new file mode 100644 index 00000000000..781798a9b52 --- /dev/null +++ b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Endurance/MetabolismEnduranceModifierSystem.cs @@ -0,0 +1,78 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Timing; +using Content.Shared.Movement.Components; +using Content.Shared.Movement.Systems; +using static Content.Shared.Nuclear14.Special.ChemistryModifiers.EnduranceModifierMetabolismComponent; +using Content.Shared.Nuclear14.Special.ChemistryModifiers; + +namespace Content.Shared.Nuclear14.Special.ChemistryModifiers; +public sealed class MetabolismEnduranceModifierSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly SpecialModifierSystem _specialModifiers = default!; + + private readonly List _components = new(); + + public override void Initialize() + { + base.Initialize(); + + UpdatesOutsidePrediction = true; + + SubscribeLocalEvent(OnMovespeedHandleState); + SubscribeLocalEvent(OnGetState); + SubscribeLocalEvent(AddComponent); + SubscribeLocalEvent(OnRefreshMovespeed); + } + + private void OnGetState(EntityUid uid, EnduranceModifierMetabolismComponent component, ref ComponentGetState args) + { + args.State = new EnduranceModifierMetabolismComponentState( + component.EnduranceModifier, + component.ModifierTimer); + } + + private void OnMovespeedHandleState(EntityUid uid, EnduranceModifierMetabolismComponent component, ref ComponentHandleState args) + { + if (args.Current is not EnduranceModifierMetabolismComponentState cast) + return; + + component.EnduranceModifier = cast.EnduranceModifier; + component.ModifierTimer = cast.ModifierTimer; + } + + private void OnRefreshMovespeed(EntityUid uid, EnduranceModifierMetabolismComponent component, RefreshSpecialModifiersEvent args) + { + args.ModifyEndurance(component.EnduranceModifier); + } + + private void AddComponent(EntityUid uid, EnduranceModifierMetabolismComponent component, ComponentStartup args) + { + _components.Add(component); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var currentTime = _gameTiming.CurTime; + + for (var i = _components.Count - 1; i >= 0; i--) + { + var component = _components[i]; + + if (component.Deleted) + { + _components.RemoveAt(i); + continue; + } + + if (component.ModifierTimer > currentTime) continue; + + _components.RemoveAt(i); + EntityManager.RemoveComponent(component.Owner); + + _specialModifiers.RefreshClothingSpecialModifiers(component.Owner); + } + } +} diff --git a/Content.Shared/Nuclear14/Special/ChemistryModifiers/Intelligence/IntelligenceModifier.cs b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Intelligence/IntelligenceModifier.cs new file mode 100644 index 00000000000..d97729a6fda --- /dev/null +++ b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Intelligence/IntelligenceModifier.cs @@ -0,0 +1,65 @@ +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.Movement.Systems; +using Robust.Shared.Prototypes; +using Robust.Shared.Timing; +using Content.Shared.Nuclear14.Special; + +namespace Content.Shared.Nuclear14.Special.ChemistryModifiers; + + /// + /// Default metabolism for stimulants and tranqs. Attempts to find a IntelligenceModifier on the target, + /// adding one if not there and to change the Intelligence + /// +public sealed partial class IntelligenceModifierReagent : ReagentEffect +{ + + [DataField("intelligenceModifier")] + public int IntelligenceModifier { get; set; } = 0; + + /// + /// How long the modifier applies (in seconds) when metabolized. + /// + [DataField("statusLifetime")] + public float StatusLifetime = 2f; + + protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) + { + return Loc.GetString("reagent-effect-guidebook-intelligence-modifier", + ("chance", Probability), + ("intelligence", IntelligenceModifier), + ("time", StatusLifetime)); + } + + /// + /// Remove reagent at set rate, changes the Intelligence modifier and adds a IntelligenceModifierMetabolismComponent if not already there. + /// + public override void Effect(ReagentEffectArgs args) + { + var status = args.EntityManager.EnsureComponent(args.SolutionEntity); + + // Only refresh movement if we need to. + var modified = !status.IntelligenceModifier.Equals(IntelligenceModifier); + + status.IntelligenceModifier = IntelligenceModifier; + + // only going to scale application time + var statusLifetime = StatusLifetime; + statusLifetime *= args.Scale; + + IncreaseTimer(status, statusLifetime); + + if (modified) + EntitySystem.Get().RefreshClothingSpecialModifiers(args.SolutionEntity); + + } + public void IncreaseTimer(IntelligenceModifierMetabolismComponent status, float time) + { + var gameTiming = IoCManager.Resolve(); + + var offsetTime = Math.Max(status.ModifierTimer.TotalSeconds, gameTiming.CurTime.TotalSeconds); + + status.ModifierTimer = TimeSpan.FromSeconds(offsetTime + time); + status.Dirty(); + } +} diff --git a/Content.Shared/Nuclear14/Special/ChemistryModifiers/Intelligence/IntelligenceModifierMetabolismComponent.cs b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Intelligence/IntelligenceModifierMetabolismComponent.cs new file mode 100644 index 00000000000..0b9b515da61 --- /dev/null +++ b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Intelligence/IntelligenceModifierMetabolismComponent.cs @@ -0,0 +1,33 @@ +using Robust.Shared.Serialization; +using Robust.Shared.GameStates; + +namespace Content.Shared.Nuclear14.Special.ChemistryModifiers; + +[RegisterComponent] +[NetworkedComponent] +public sealed partial class IntelligenceModifierMetabolismComponent : Component +{ + [ViewVariables] + public int IntelligenceModifier { get; set; } + + /// + /// When the current modifier is expected to end. + /// + [ViewVariables] + public TimeSpan ModifierTimer { get; set; } = TimeSpan.Zero; + + [Serializable, NetSerializable] + public sealed class IntelligenceModifierMetabolismComponentState : ComponentState + { + public int IntelligenceModifier { get; } + public TimeSpan ModifierTimer { get; } + + public IntelligenceModifierMetabolismComponentState(int intelligenceModifier, TimeSpan modifierTimer) + { + IntelligenceModifier = intelligenceModifier; + ModifierTimer = modifierTimer; + } + } +} + + diff --git a/Content.Shared/Nuclear14/Special/ChemistryModifiers/Intelligence/MetabolismIntelligenceModifierSystem.cs b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Intelligence/MetabolismIntelligenceModifierSystem.cs new file mode 100644 index 00000000000..b71e0522680 --- /dev/null +++ b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Intelligence/MetabolismIntelligenceModifierSystem.cs @@ -0,0 +1,78 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Timing; +using Content.Shared.Movement.Components; +using Content.Shared.Movement.Systems; +using static Content.Shared.Nuclear14.Special.ChemistryModifiers.IntelligenceModifierMetabolismComponent; +using Content.Shared.Nuclear14.Special.ChemistryModifiers; + +namespace Content.Shared.Nuclear14.Special.ChemistryModifiers; +public sealed class MetabolismIntelligenceModifierSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly SpecialModifierSystem _specialModifiers = default!; + + private readonly List _components = new(); + + public override void Initialize() + { + base.Initialize(); + + UpdatesOutsidePrediction = true; + + SubscribeLocalEvent(OnMovespeedHandleState); + SubscribeLocalEvent(OnGetState); + SubscribeLocalEvent(AddComponent); + SubscribeLocalEvent(OnRefreshMovespeed); + } + + private void OnGetState(EntityUid uid, IntelligenceModifierMetabolismComponent component, ref ComponentGetState args) + { + args.State = new IntelligenceModifierMetabolismComponentState( + component.IntelligenceModifier, + component.ModifierTimer); + } + + private void OnMovespeedHandleState(EntityUid uid, IntelligenceModifierMetabolismComponent component, ref ComponentHandleState args) + { + if (args.Current is not IntelligenceModifierMetabolismComponentState cast) + return; + + component.IntelligenceModifier = cast.IntelligenceModifier; + component.ModifierTimer = cast.ModifierTimer; + } + + private void OnRefreshMovespeed(EntityUid uid, IntelligenceModifierMetabolismComponent component, RefreshSpecialModifiersEvent args) + { + args.ModifyIntelligence(component.IntelligenceModifier); + } + + private void AddComponent(EntityUid uid, IntelligenceModifierMetabolismComponent component, ComponentStartup args) + { + _components.Add(component); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var currentTime = _gameTiming.CurTime; + + for (var i = _components.Count - 1; i >= 0; i--) + { + var component = _components[i]; + + if (component.Deleted) + { + _components.RemoveAt(i); + continue; + } + + if (component.ModifierTimer > currentTime) continue; + + _components.RemoveAt(i); + EntityManager.RemoveComponent(component.Owner); + + _specialModifiers.RefreshClothingSpecialModifiers(component.Owner); + } + } +} diff --git a/Content.Shared/Nuclear14/Special/ChemistryModifiers/Luck/LuckModifier.cs b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Luck/LuckModifier.cs new file mode 100644 index 00000000000..9323091ea01 --- /dev/null +++ b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Luck/LuckModifier.cs @@ -0,0 +1,65 @@ +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.Movement.Systems; +using Robust.Shared.Prototypes; +using Robust.Shared.Timing; +using Content.Shared.Nuclear14.Special; + +namespace Content.Shared.Nuclear14.Special.ChemistryModifiers; + + /// + /// Default metabolism for stimulants and tranqs. Attempts to find a LuckModifier on the target, + /// adding one if not there and to change the Luck + /// +public sealed partial class LuckModifierReagent : ReagentEffect +{ + + [DataField("luckModifier")] + public int LuckModifier { get; set; } = 0; + + /// + /// How long the modifier applies (in seconds) when metabolized. + /// + [DataField("statusLifetime")] + public float StatusLifetime = 2f; + + protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) + { + return Loc.GetString("reagent-effect-guidebook-luck-modifier", + ("chance", Probability), + ("luck", LuckModifier), + ("time", StatusLifetime)); + } + + /// + /// Remove reagent at set rate, changes the Luck modifier and adds a LuckModifierMetabolismComponent if not already there. + /// + public override void Effect(ReagentEffectArgs args) + { + var status = args.EntityManager.EnsureComponent(args.SolutionEntity); + + // Only refresh movement if we need to. + var modified = !status.LuckModifier.Equals(LuckModifier); + + status.LuckModifier = LuckModifier; + + // only going to scale application time + var statusLifetime = StatusLifetime; + statusLifetime *= args.Scale; + + IncreaseTimer(status, statusLifetime); + + if (modified) + EntitySystem.Get().RefreshClothingSpecialModifiers(args.SolutionEntity); + + } + public void IncreaseTimer(LuckModifierMetabolismComponent status, float time) + { + var gameTiming = IoCManager.Resolve(); + + var offsetTime = Math.Max(status.ModifierTimer.TotalSeconds, gameTiming.CurTime.TotalSeconds); + + status.ModifierTimer = TimeSpan.FromSeconds(offsetTime + time); + status.Dirty(); + } +} diff --git a/Content.Shared/Nuclear14/Special/ChemistryModifiers/Luck/LuckModifierMetabolismComponent.cs b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Luck/LuckModifierMetabolismComponent.cs new file mode 100644 index 00000000000..5ab8379f3b7 --- /dev/null +++ b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Luck/LuckModifierMetabolismComponent.cs @@ -0,0 +1,33 @@ +using Robust.Shared.Serialization; +using Robust.Shared.GameStates; + +namespace Content.Shared.Nuclear14.Special.ChemistryModifiers; + +[RegisterComponent] +[NetworkedComponent] +public sealed partial class LuckModifierMetabolismComponent : Component +{ + [ViewVariables] + public int LuckModifier { get; set; } + + /// + /// When the current modifier is expected to end. + /// + [ViewVariables] + public TimeSpan ModifierTimer { get; set; } = TimeSpan.Zero; + + [Serializable, NetSerializable] + public sealed class LuckModifierMetabolismComponentState : ComponentState + { + public int LuckModifier { get; } + public TimeSpan ModifierTimer { get; } + + public LuckModifierMetabolismComponentState(int luckModifier, TimeSpan modifierTimer) + { + LuckModifier = luckModifier; + ModifierTimer = modifierTimer; + } + } +} + + diff --git a/Content.Shared/Nuclear14/Special/ChemistryModifiers/Luck/MetabolismLuckModifierSystem.cs b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Luck/MetabolismLuckModifierSystem.cs new file mode 100644 index 00000000000..f19e4813e67 --- /dev/null +++ b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Luck/MetabolismLuckModifierSystem.cs @@ -0,0 +1,78 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Timing; +using Content.Shared.Movement.Components; +using Content.Shared.Movement.Systems; +using static Content.Shared.Nuclear14.Special.ChemistryModifiers.LuckModifierMetabolismComponent; +using Content.Shared.Nuclear14.Special.ChemistryModifiers; + +namespace Content.Shared.Nuclear14.Special.ChemistryModifiers; +public sealed partial class MetabolismLuckModifierSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly SpecialModifierSystem _specialModifiers = default!; + + private readonly List _components = new(); + + public override void Initialize() + { + base.Initialize(); + + UpdatesOutsidePrediction = true; + + SubscribeLocalEvent(OnMovespeedHandleState); + SubscribeLocalEvent(OnGetState); + SubscribeLocalEvent(AddComponent); + SubscribeLocalEvent(OnRefreshMovespeed); + } + + private void OnGetState(EntityUid uid, LuckModifierMetabolismComponent component, ref ComponentGetState args) + { + args.State = new LuckModifierMetabolismComponentState( + component.LuckModifier, + component.ModifierTimer); + } + + private void OnMovespeedHandleState(EntityUid uid, LuckModifierMetabolismComponent component, ref ComponentHandleState args) + { + if (args.Current is not LuckModifierMetabolismComponentState cast) + return; + + component.LuckModifier = cast.LuckModifier; + component.ModifierTimer = cast.ModifierTimer; + } + + private void OnRefreshMovespeed(EntityUid uid, LuckModifierMetabolismComponent component, RefreshSpecialModifiersEvent args) + { + args.ModifyLuck(component.LuckModifier); + } + + private void AddComponent(EntityUid uid, LuckModifierMetabolismComponent component, ComponentStartup args) + { + _components.Add(component); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var currentTime = _gameTiming.CurTime; + + for (var i = _components.Count - 1; i >= 0; i--) + { + var component = _components[i]; + + if (component.Deleted) + { + _components.RemoveAt(i); + continue; + } + + if (component.ModifierTimer > currentTime) continue; + + _components.RemoveAt(i); + EntityManager.RemoveComponent(component.Owner); + + _specialModifiers.RefreshClothingSpecialModifiers(component.Owner); + } + } +} diff --git a/Content.Shared/Nuclear14/Special/ChemistryModifiers/Perception/MetabolismPerceptionModifierSystem.cs b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Perception/MetabolismPerceptionModifierSystem.cs new file mode 100644 index 00000000000..a4e77a71310 --- /dev/null +++ b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Perception/MetabolismPerceptionModifierSystem.cs @@ -0,0 +1,78 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Timing; +using Content.Shared.Movement.Components; +using Content.Shared.Movement.Systems; +using static Content.Shared.Nuclear14.Special.ChemistryModifiers.PerceptionModifierMetabolismComponent; +using Content.Shared.Nuclear14.Special.ChemistryModifiers; + +namespace Content.Shared.Nuclear14.Special.ChemistryModifiers; +public sealed partial class MetabolismPerceptionModifierSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly SpecialModifierSystem _specialModifiers = default!; + + private readonly List _components = new(); + + public override void Initialize() + { + base.Initialize(); + + UpdatesOutsidePrediction = true; + + SubscribeLocalEvent(OnMovespeedHandleState); + SubscribeLocalEvent(OnGetState); + SubscribeLocalEvent(AddComponent); + SubscribeLocalEvent(OnRefreshMovespeed); + } + + private void OnGetState(EntityUid uid, PerceptionModifierMetabolismComponent component, ref ComponentGetState args) + { + args.State = new PerceptionModifierMetabolismComponentState( + component.PerceptionModifier, + component.ModifierTimer); + } + + private void OnMovespeedHandleState(EntityUid uid, PerceptionModifierMetabolismComponent component, ref ComponentHandleState args) + { + if (args.Current is not PerceptionModifierMetabolismComponentState cast) + return; + + component.PerceptionModifier = cast.PerceptionModifier; + component.ModifierTimer = cast.ModifierTimer; + } + + private void OnRefreshMovespeed(EntityUid uid, PerceptionModifierMetabolismComponent component, RefreshSpecialModifiersEvent args) + { + args.ModifyPerception(component.PerceptionModifier); + } + + private void AddComponent(EntityUid uid, PerceptionModifierMetabolismComponent component, ComponentStartup args) + { + _components.Add(component); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var currentTime = _gameTiming.CurTime; + + for (var i = _components.Count - 1; i >= 0; i--) + { + var component = _components[i]; + + if (component.Deleted) + { + _components.RemoveAt(i); + continue; + } + + if (component.ModifierTimer > currentTime) continue; + + _components.RemoveAt(i); + EntityManager.RemoveComponent(component.Owner); + + _specialModifiers.RefreshClothingSpecialModifiers(component.Owner); + } + } +} diff --git a/Content.Shared/Nuclear14/Special/ChemistryModifiers/Perception/PerceptionModifier.cs b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Perception/PerceptionModifier.cs new file mode 100644 index 00000000000..40c11cba6d0 --- /dev/null +++ b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Perception/PerceptionModifier.cs @@ -0,0 +1,65 @@ +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.Movement.Systems; +using Robust.Shared.Prototypes; +using Robust.Shared.Timing; +using Content.Shared.Nuclear14.Special; + +namespace Content.Shared.Nuclear14.Special.ChemistryModifiers; + + /// + /// Default metabolism for stimulants and tranqs. Attempts to find a PerceptionModifier on the target, + /// adding one if not there and to change the perception + /// +public sealed partial class PerceptionModifierReagent : ReagentEffect +{ + + [DataField("perceptionModifier")] + public int PerceptionModifier { get; set; } = 0; + + /// + /// How long the modifier applies (in seconds) when metabolized. + /// + [DataField("statusLifetime")] + public float StatusLifetime = 2f; + + protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) + { + return Loc.GetString("reagent-effect-guidebook-perception-modifier", + ("chance", Probability), + ("perception", PerceptionModifier), + ("time", StatusLifetime)); + } + + /// + /// Remove reagent at set rate, changes the perception modifier and adds a PerceptionModifierMetabolismComponent if not already there. + /// + public override void Effect(ReagentEffectArgs args) + { + var status = args.EntityManager.EnsureComponent(args.SolutionEntity); + + // Only refresh movement if we need to. + var modified = !status.PerceptionModifier.Equals(PerceptionModifier); + + status.PerceptionModifier = PerceptionModifier; + + // only going to scale application time + var statusLifetime = StatusLifetime; + statusLifetime *= args.Scale; + + IncreaseTimer(status, statusLifetime); + + if (modified) + EntitySystem.Get().RefreshClothingSpecialModifiers(args.SolutionEntity); + + } + public void IncreaseTimer(PerceptionModifierMetabolismComponent status, float time) + { + var gameTiming = IoCManager.Resolve(); + + var offsetTime = Math.Max(status.ModifierTimer.TotalSeconds, gameTiming.CurTime.TotalSeconds); + + status.ModifierTimer = TimeSpan.FromSeconds(offsetTime + time); + status.Dirty(); + } +} diff --git a/Content.Shared/Nuclear14/Special/ChemistryModifiers/Perception/PerceptionModifierMetabolismComponent.cs b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Perception/PerceptionModifierMetabolismComponent.cs new file mode 100644 index 00000000000..4aae4798410 --- /dev/null +++ b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Perception/PerceptionModifierMetabolismComponent.cs @@ -0,0 +1,33 @@ +using Robust.Shared.Serialization; +using Robust.Shared.GameStates; + +namespace Content.Shared.Nuclear14.Special.ChemistryModifiers; + +[RegisterComponent] +[NetworkedComponent] +public sealed partial class PerceptionModifierMetabolismComponent : Component +{ + [ViewVariables] + public int PerceptionModifier { get; set; } + + /// + /// When the current modifier is expected to end. + /// + [ViewVariables] + public TimeSpan ModifierTimer { get; set; } = TimeSpan.Zero; + + [Serializable, NetSerializable] + public sealed class PerceptionModifierMetabolismComponentState : ComponentState + { + public int PerceptionModifier { get; } + public TimeSpan ModifierTimer { get; } + + public PerceptionModifierMetabolismComponentState(int perceptionModifier, TimeSpan modifierTimer) + { + PerceptionModifier = perceptionModifier; + ModifierTimer = modifierTimer; + } + } +} + + diff --git a/Content.Shared/Nuclear14/Special/ChemistryModifiers/Strength/MetabolismStrengthModifierSystem.cs b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Strength/MetabolismStrengthModifierSystem.cs new file mode 100644 index 00000000000..6a06d2c9d23 --- /dev/null +++ b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Strength/MetabolismStrengthModifierSystem.cs @@ -0,0 +1,78 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Timing; +using Content.Shared.Movement.Components; +using Content.Shared.Movement.Systems; +using static Content.Shared.Nuclear14.Special.ChemistryModifiers.StrengthModifierMetabolismComponent; +using Content.Shared.Nuclear14.Special.ChemistryModifiers; + +namespace Content.Shared.Nuclear14.Special.ChemistryModifiers; +public sealed partial class MetabolismStrengthModifierSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly SpecialModifierSystem _specialModifiers = default!; + + private readonly List _components = new(); + + public override void Initialize() + { + base.Initialize(); + + UpdatesOutsidePrediction = true; + + SubscribeLocalEvent(OnMovespeedHandleState); + SubscribeLocalEvent(OnGetState); + SubscribeLocalEvent(AddComponent); + SubscribeLocalEvent(OnRefreshMovespeed); + } + + private void OnGetState(EntityUid uid, StrengthModifierMetabolismComponent component, ref ComponentGetState args) + { + args.State = new StrengthModifierMetabolismComponentState( + component.StrengthModifier, + component.ModifierTimer); + } + + private void OnMovespeedHandleState(EntityUid uid, StrengthModifierMetabolismComponent component, ref ComponentHandleState args) + { + if (args.Current is not StrengthModifierMetabolismComponentState cast) + return; + + component.StrengthModifier = cast.StrengthModifier; + component.ModifierTimer = cast.ModifierTimer; + } + + private void OnRefreshMovespeed(EntityUid uid, StrengthModifierMetabolismComponent component, RefreshSpecialModifiersEvent args) + { + args.ModifyStrength(component.StrengthModifier); + } + + private void AddComponent(EntityUid uid, StrengthModifierMetabolismComponent component, ComponentStartup args) + { + _components.Add(component); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var currentTime = _gameTiming.CurTime; + + for (var i = _components.Count - 1; i >= 0; i--) + { + var component = _components[i]; + + if (component.Deleted) + { + _components.RemoveAt(i); + continue; + } + + if (component.ModifierTimer > currentTime) continue; + + _components.RemoveAt(i); + EntityManager.RemoveComponent(component.Owner); + + _specialModifiers.RefreshClothingSpecialModifiers(component.Owner); + } + } +} diff --git a/Content.Shared/Nuclear14/Special/ChemistryModifiers/Strength/StrengthModifier.cs b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Strength/StrengthModifier.cs new file mode 100644 index 00000000000..cfcbc25e41a --- /dev/null +++ b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Strength/StrengthModifier.cs @@ -0,0 +1,65 @@ +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.Movement.Systems; +using Robust.Shared.Prototypes; +using Robust.Shared.Timing; +using Content.Shared.Nuclear14.Special; + +namespace Content.Shared.Nuclear14.Special.ChemistryModifiers; + + /// + /// Default metabolism for stimulants and tranqs. Attempts to find a StrengthModifier on the target, + /// adding one if not there and to change the strength + /// +public sealed partial class StrengthModifierReagent : ReagentEffect +{ + + [DataField("strengthModifier")] + public int StrengthModifier { get; set; } = 0; + + /// + /// How long the modifier applies (in seconds) when metabolized. + /// + [DataField("statusLifetime")] + public float StatusLifetime = 2f; + + protected override string? ReagentEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) + { + return Loc.GetString("reagent-effect-guidebook-strength-modifier", + ("chance", Probability), + ("strength", StrengthModifier), + ("time", StatusLifetime)); + } + + /// + /// Remove reagent at set rate, changes the strength modifier and adds a StrengthModifierMetabolismComponent if not already there. + /// + public override void Effect(ReagentEffectArgs args) + { + var status = args.EntityManager.EnsureComponent(args.SolutionEntity); + + // Only refresh movement if we need to. + var modified = !status.StrengthModifier.Equals(StrengthModifier); + + status.StrengthModifier = StrengthModifier; + + // only going to scale application time + var statusLifetime = StatusLifetime; + statusLifetime *= args.Scale; + + IncreaseTimer(status, statusLifetime); + + if (modified) + EntitySystem.Get().RefreshClothingSpecialModifiers(args.SolutionEntity); + + } + public void IncreaseTimer(StrengthModifierMetabolismComponent status, float time) + { + var gameTiming = IoCManager.Resolve(); + + var offsetTime = Math.Max(status.ModifierTimer.TotalSeconds, gameTiming.CurTime.TotalSeconds); + + status.ModifierTimer = TimeSpan.FromSeconds(offsetTime + time); + status.Dirty(); + } +} diff --git a/Content.Shared/Nuclear14/Special/ChemistryModifiers/Strength/StrengthModifierMetabolismComponent.cs b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Strength/StrengthModifierMetabolismComponent.cs new file mode 100644 index 00000000000..36c43d7a766 --- /dev/null +++ b/Content.Shared/Nuclear14/Special/ChemistryModifiers/Strength/StrengthModifierMetabolismComponent.cs @@ -0,0 +1,33 @@ +using Robust.Shared.Serialization; +using Robust.Shared.GameStates; + +namespace Content.Shared.Nuclear14.Special.ChemistryModifiers; + +[RegisterComponent] +[NetworkedComponent] +public sealed partial class StrengthModifierMetabolismComponent : Component +{ + [ViewVariables] + public int StrengthModifier { get; set; } + + /// + /// When the current modifier is expected to end. + /// + [ViewVariables] + public TimeSpan ModifierTimer { get; set; } = TimeSpan.Zero; + + [Serializable, NetSerializable] + public sealed class StrengthModifierMetabolismComponentState : ComponentState + { + public int StrengthModifier { get; } + public TimeSpan ModifierTimer { get; } + + public StrengthModifierMetabolismComponentState(int strengthModifier, TimeSpan modifierTimer) + { + StrengthModifier = strengthModifier; + ModifierTimer = modifierTimer; + } + } +} + + diff --git a/Content.Shared/Nuclear14/Special/ClothingSpecialModifierSystem.cs b/Content.Shared/Nuclear14/Special/ClothingSpecialModifierSystem.cs new file mode 100644 index 00000000000..fd354e370b6 --- /dev/null +++ b/Content.Shared/Nuclear14/Special/ClothingSpecialModifierSystem.cs @@ -0,0 +1,199 @@ +using Content.Shared.Examine; +using Content.Shared.Inventory; +using Content.Shared.Verbs; +using Robust.Shared.Containers; +using Robust.Shared.GameStates; +using Robust.Shared.Utility; +using Content.Shared.Nuclear14.Special; +using Content.Shared.Nuclear14.Special.Components; + +namespace Content.Shared.Nuclear14.Special; + +public sealed partial class ClothingSpecialModifierSystem : EntitySystem +{ + [Dependency] private readonly SpecialModifierSystem _specialModifiers = default!; + + [Dependency] private readonly SharedContainerSystem _container = default!; + [Dependency] private readonly ExamineSystemShared _examine = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnGetState); + SubscribeLocalEvent(OnHandleState); + SubscribeLocalEvent>(OnRefreshModifiers); + SubscribeLocalEvent>(OnClothingVerbExamine); + } + + // Public API + + public void SetClothingSpecialModifierEnabled(EntityUid uid, bool enabled, ClothingSpecialModifierComponent? component = null) + { + if (!Resolve(uid, ref component, false)) + return; + + if (component.Enabled != enabled) + { + component.Enabled = enabled; + Dirty(component); + + // inventory system will automatically hook into the event raised by this and update accordingly + if (_container.TryGetContainingContainer(uid, out var container)) + { + _specialModifiers.RefreshClothingSpecialModifiers(container.Owner); + } + } + } + + // Event handlers + + private void OnGetState(EntityUid uid, ClothingSpecialModifierComponent component, ref ComponentGetState args) + { + args.State = new ClothingSpecialModifierComponentState( + component.StrengthModifier, + component.PerceptionModifier, + component.EnduranceModifier, + component.CharismaModifier, + component.IntelligenceModifier, + component.AgilityModifier, + component.LuckModifier, + component.Enabled); + } + + private void OnHandleState(EntityUid uid, ClothingSpecialModifierComponent component, ref ComponentHandleState args) + { + if (args.Current is not ClothingSpecialModifierComponentState state) + return; + + var diff = component.Enabled != state.Enabled || + !MathHelper.CloseTo(component.StrengthModifier, state.StrengthModifier) || + !MathHelper.CloseTo(component.PerceptionModifier, state.PerceptionModifier) || + !MathHelper.CloseTo(component.EnduranceModifier, state.EnduranceModifier) || + !MathHelper.CloseTo(component.CharismaModifier, state.CharismaModifier) || + !MathHelper.CloseTo(component.IntelligenceModifier, state.IntelligenceModifier) || + !MathHelper.CloseTo(component.AgilityModifier, state.AgilityModifier) || + !MathHelper.CloseTo(component.LuckModifier, state.LuckModifier); + + component.StrengthModifier = state.StrengthModifier; + component.PerceptionModifier = state.PerceptionModifier; + component.EnduranceModifier = state.EnduranceModifier; + component.CharismaModifier = state.CharismaModifier; + component.IntelligenceModifier = state.IntelligenceModifier; + component.AgilityModifier = state.AgilityModifier; + component.LuckModifier = state.LuckModifier; + component.Enabled = state.Enabled; + + // Avoid raising the event for the container if nothing changed. + // We'll still set the values in case they're slightly different but within tolerance. + if (diff && _container.TryGetContainingContainer(uid, out var container)) + { + _specialModifiers.RefreshClothingSpecialModifiers(container.Owner); + } + } + + private void OnRefreshModifiers(EntityUid uid, ClothingSpecialModifierComponent component, InventoryRelayedEvent args) + { + if (!component.Enabled) + return; + + args.Args.ModifySpecial(component.StrengthModifier, + component.PerceptionModifier, + component.EnduranceModifier, + component.CharismaModifier, + component.IntelligenceModifier, + component.AgilityModifier, + component.LuckModifier + ); + } + + private void OnClothingVerbExamine(EntityUid uid, ClothingSpecialModifierComponent component, GetVerbsEvent args) + { + if (!args.CanInteract || !args.CanAccess) + return; + + var strengthModifier = component.StrengthModifier; + var perceptionModifier = component.PerceptionModifier; + var enduranceModifier = component.EnduranceModifier; + var charismaModifier = component.CharismaModifier; + var intelligenceModifier = component.IntelligenceModifier; + var agilityModifier = component.AgilityModifier; + var luckModifier = component.LuckModifier; + + var msg = new FormattedMessage(); + + if (strengthModifier != 0){ + if (strengthModifier > 0){ + msg.AddMarkup(Loc.GetString("clothing-strength-increase-equal-examine", ("strength", strengthModifier))); + } + else if (strengthModifier < 0) + msg.AddMarkup(Loc.GetString("clothing-strength-decrease-equal-examine", ("strength", strengthModifier))); + msg.PushNewline(); + } + + if (perceptionModifier != 0){ + if (perceptionModifier > 0) + msg.AddMarkup(Loc.GetString("clothing-perception-increase-equal-examine", ("perception", perceptionModifier))); + else if (perceptionModifier < 0) + msg.AddMarkup(Loc.GetString("clothing-perception-decrease-equal-examine", ("perception", perceptionModifier))); + msg.PushNewline(); + } + + if (enduranceModifier != 0){ + if (enduranceModifier > 0) + msg.AddMarkup(Loc.GetString("clothing-endurance-increase-equal-examine", ("endurance", enduranceModifier))); + else if (enduranceModifier < 0) + msg.AddMarkup(Loc.GetString("clothing-endurance-decrease-equal-examine", ("endurance", enduranceModifier))); + msg.PushNewline(); + } + + if (charismaModifier != 0){ + if (charismaModifier > 0) + msg.AddMarkup(Loc.GetString("clothing-charisma-increase-equal-examine", ("charisma", charismaModifier))); + else if (charismaModifier < 0) + msg.AddMarkup(Loc.GetString("clothing-charisma-decrease-equal-examine", ("charisma", charismaModifier))); + msg.PushNewline(); + } + + if (intelligenceModifier != 0){ + if (intelligenceModifier > 0) + msg.AddMarkup(Loc.GetString("clothing-intelligence-increase-equal-examine", ("intelligence", intelligenceModifier))); + else if (intelligenceModifier < 0) + msg.AddMarkup(Loc.GetString("clothing-intelligence-decrease-equal-examine", ("intelligence", intelligenceModifier))); + msg.PushNewline(); + } + + if (agilityModifier != 0){ + if (agilityModifier > 0) + msg.AddMarkup(Loc.GetString("clothing-agility-increase-equal-examine", ("agility", agilityModifier))); + else if (agilityModifier < 0) + msg.AddMarkup(Loc.GetString("clothing-agility-decrease-equal-examine", ("agility", agilityModifier))); + msg.PushNewline(); + } + + if (luckModifier != 0){ + if (luckModifier > 0) + msg.AddMarkup(Loc.GetString("clothing-luck-increase-equal-examine", ("luck", luckModifier))); + else if (luckModifier < 0) + msg.AddMarkup(Loc.GetString("clothing-luck-decrease-equal-examine", ("luck", luckModifier))); + msg.PushNewline(); + } + + if (strengthModifier != 0 || + perceptionModifier != 0 || + enduranceModifier != 0 || + enduranceModifier != 0 || + charismaModifier != 0 || + intelligenceModifier != 0 || + agilityModifier != 0 || + luckModifier != 0 + ) + _examine.AddDetailedExamineVerb(args, + component, + msg, + Loc.GetString("clothing-special-examinable-verb-text"), + "/Textures/Interface/VerbIcons/outfit.svg.192dpi.png", + Loc.GetString("clothing-special-examinable-verb-message") + ); + } +} diff --git a/Content.Shared/Nuclear14/Special/Components/ClothingSpecialModifierComponent.cs b/Content.Shared/Nuclear14/Special/Components/ClothingSpecialModifierComponent.cs new file mode 100644 index 00000000000..a1f2d3ea8ff --- /dev/null +++ b/Content.Shared/Nuclear14/Special/Components/ClothingSpecialModifierComponent.cs @@ -0,0 +1,116 @@ +using System.Numerics; +using Content.Shared.FixedPoint; +using Content.Shared.Store; +using Content.Shared.Whitelist; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Robust.Shared.Serialization; + +namespace Content.Shared.Nuclear14.Special.Components; + +[RegisterComponent, NetworkedComponent, Access(typeof(ClothingSpecialModifierSystem))] +public sealed partial class ClothingSpecialModifierComponent : Component +{ + #region Special stats + + /// + /// SPECIAL Strength of cloth boost + /// + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("strengthModifier")] + public int StrengthModifier = 0; + + /// + /// SPECIAL Perception of cloth boost + /// + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("perceptionModifier")] + public int PerceptionModifier = 0; + + /// + /// SPECIAL Endurance of cloth boost + /// + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("enduranceModifier")] + public int EnduranceModifier = 0; + + /// + /// SPECIAL Charisma of cloth boost + /// + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("charismaModifier")] + public int CharismaModifier = 0; + + /// + /// SPECIAL Intelligence of cloth boost + /// + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("intelligenceModifier")] + public int IntelligenceModifier = 0; + + /// + /// SPECIAL Agility of cloth boost + /// + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("agilityModifier")] + public int AgilityModifier = 0; + + /// + /// SPECIAL Luck of cloth boost + /// + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("luckModifier")] + public int LuckModifier = 0; + + /// + /// Is this clothing item currently 'actively' affects you? + /// e.g. magboots can be turned on and off. + /// e.g. or clothing using something battery which could become dead + /// + [DataField("enabled")] public bool Enabled = true; + + + #endregion + +} + +[Serializable, NetSerializable] +public sealed class ClothingSpecialModifierComponentState : ComponentState +{ + public int StrengthModifier; + public int PerceptionModifier; + public int EnduranceModifier; + public int CharismaModifier; + public int IntelligenceModifier; + public int AgilityModifier; + public int LuckModifier; + public bool Enabled; + + public ClothingSpecialModifierComponentState( + int strengthModifier, + int perceptionModifier, + int enduranceModifier, + int charismaModifier, + int intelligenceModifier, + int agilityModifier, + int luckModifier, + bool enabled) + { + StrengthModifier = strengthModifier; + PerceptionModifier = perceptionModifier; + EnduranceModifier = enduranceModifier; + CharismaModifier = charismaModifier; + IntelligenceModifier = intelligenceModifier; + AgilityModifier = agilityModifier; + LuckModifier = luckModifier; + Enabled = enabled; + } +} diff --git a/Content.Shared/Nuclear14/Special/Components/SpecialComponent.cs b/Content.Shared/Nuclear14/Special/Components/SpecialComponent.cs new file mode 100644 index 00000000000..e960ca7db20 --- /dev/null +++ b/Content.Shared/Nuclear14/Special/Components/SpecialComponent.cs @@ -0,0 +1,123 @@ +using System.Numerics; +using Content.Shared.FixedPoint; +using Content.Shared.Store; +using Content.Shared.Whitelist; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Shared.Nuclear14.Special.Components; + +[RegisterComponent, NetworkedComponent]//, AutoGenerateComponentState] +public sealed partial class SpecialComponent : Component +{ + #region Special Base Stats + + /// + /// SPECIAL Strength of player + /// + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("strength")] + public int BaseStrength { get; set; } = 5; + + [ViewVariables(VVAccess.ReadWrite)] + [DataField("perception")] + public int BasePerception { get; set; } = 5; + + [ViewVariables(VVAccess.ReadWrite)] + [DataField("endurance")] + public int BaseEndurance { get; set; } = 5; + + [ViewVariables(VVAccess.ReadWrite)] + [DataField("charisma")] + public int BaseCharisma { get; set; } = 5; + + [ViewVariables(VVAccess.ReadWrite)] + [DataField("intelligence")] + public int BaseIntelligence { get; set; } = 5; + + [ViewVariables(VVAccess.ReadWrite)] + [DataField("agility")] + public int BaseAgility { get; set; } = 5; + + [ViewVariables(VVAccess.ReadWrite)] + [DataField("luck")] + public int BaseLuck { get; set; } = 5; + + #endregion + + #region Special modifiers + + [ViewVariables(VVAccess.ReadWrite)] + [DataField("strengthModifier")] + public int StrengthModifier { get; set; } = 0; + + [ViewVariables(VVAccess.ReadWrite)] + [DataField("perceptionModifier")] + public int PerceptionModifier { get; set; } = 0; + + [ViewVariables(VVAccess.ReadWrite)] + [DataField("enduranceModifier")] + public int EnduranceModifier { get; set; } = 0; + + [ViewVariables(VVAccess.ReadWrite)] + [DataField("charismaModifier")] + public int CharismaModifier { get; set; } = 0; + + [ViewVariables(VVAccess.ReadWrite)] + [DataField("intelligenceModifier")] + public int IntelligenceModifier { get; set; } = 0; + + [ViewVariables(VVAccess.ReadWrite)] + [DataField("agilityModifier")] + public int AgilityModifier { get; set; } = 0; + + [ViewVariables(VVAccess.ReadWrite)] + [DataField("luckModifier")] + public int LuckModifier { get; set; } = 0; + + #endregion + + #region Total Special + + [ViewVariables(VVAccess.ReadWrite)] + public int TotalStrength { get { return Math.Clamp(BaseStrength + StrengthModifier, SpecialAmountMin, SpecialAmountMax); } } + + [ViewVariables(VVAccess.ReadWrite)] + public int TotalPerception { get { return Math.Clamp(BasePerception + PerceptionModifier, SpecialAmountMin, SpecialAmountMax); } } + + [ViewVariables(VVAccess.ReadWrite)] + public int TotalEndurance { get { return Math.Clamp(BaseEndurance + EnduranceModifier, SpecialAmountMin, SpecialAmountMax); } } + + [ViewVariables(VVAccess.ReadWrite)] + public int TotalCharisma { get { return Math.Clamp(BaseCharisma + CharismaModifier, SpecialAmountMin, SpecialAmountMax); } } + + [ViewVariables(VVAccess.ReadWrite)] + public int TotalIntelligence { get { return Math.Clamp(BaseIntelligence + IntelligenceModifier, SpecialAmountMin, SpecialAmountMax); } } + + [ViewVariables(VVAccess.ReadWrite)] + public int TotalAgility { get { return Math.Clamp(BaseAgility + AgilityModifier, SpecialAmountMin, SpecialAmountMax); } } + + [ViewVariables(VVAccess.ReadWrite)] + public int TotalLuck { get { return Math.Clamp(BaseLuck + LuckModifier, SpecialAmountMin, SpecialAmountMax); } } + + #endregion + + #region Special min/max amount + + /// + /// Don't let Special go above this value. + /// + [ViewVariables(VVAccess.ReadOnly)]//, AutoNetworkedField] + public int SpecialAmountMax = 10; + + /// + /// Don't let Special go below this value. + /// + [ViewVariables(VVAccess.ReadOnly)]//, AutoNetworkedField] + public int SpecialAmountMin = 1; + + #endregion + +} diff --git a/Content.Shared/Nuclear14/Special/EntitySystems/StrengthSystem.cs b/Content.Shared/Nuclear14/Special/EntitySystems/StrengthSystem.cs new file mode 100644 index 00000000000..7446bd05806 --- /dev/null +++ b/Content.Shared/Nuclear14/Special/EntitySystems/StrengthSystem.cs @@ -0,0 +1,80 @@ +// NOTE: NOT USED CURRENTLY, BUT MAYBE NEEDED LATER? REMOVE/USE/CHANGE BEFORE CHANGING DRAFT STATUS! + +// using Content.Shared.Nuclear14.Special.Components; + + +// namespace Content.Shared.Nuclear.Special.Strength.EntitySystems; + +// public sealed class StrengthSystem : EntitySystem +// { + +// [Dependency] private readonly IEntityManager _entity = default!; + +// public StrengthSystem() +// { +// } + +// /// +// /// Tries to add to the Strength level of player. +// /// +// /// The entity uid. +// /// The amount to add to the strength level. +// public bool TryAddStrengthAmount(EntityUid uid, int amount) +// { +// // Check if the entity has a Special Component +// if (!_entity.TryGetComponent(uid, out _)) +// return false; + +// // Set the new power level +// AddStrengthAmount(uid, amount); + +// return true; +// } + +// /// +// /// Adds to the strength level. +// /// +// /// The entity uid. +// /// The amount to add to the power level. +// public void AddStrengthAmount(EntityUid uid, int amount) +// { +// // Get Strength component +// if (!_entity.TryGetComponent(uid, out var component)) +// { +// Logger.Error("Tried to add to strength level of entity without Special component."); +// return; +// } + +// // Get new power level +// var newStrengthAmount = component.BaseStrength + amount; + +// // Clamp power level using clamp function +// newStrengthAmount = Math.Clamp(newStrengthAmount, component.SpecialAmountMin, component.SpecialAmountMax); + +// // Set the new power level +// SetStrengthAmount(uid, newStrengthAmount); +// } + + +// /// +// /// Sets the strength level of a player. +// /// +// /// The entity uid. +// /// The new strength level. +// public void SetStrengthAmount(EntityUid uid, int newStrengthAmount) +// { +// // Get Strength component +// if (!_entity.TryGetComponent(uid, out var component)) +// { +// Logger.Error("Tried to set Strength level of entity without Strength component."); +// return; +// } + +// // Clamp strength level using clamp function to not have less than 1 or more than 10 +// newStrengthAmount = Math.Clamp(newStrengthAmount, component.SpecialAmountMin, component.SpecialAmountMax); + +// // Set the new strength level +// component.BaseStrength = newStrengthAmount; +// } + +// } diff --git a/Content.Shared/Nuclear14/Special/SpecialModifierSystem.cs b/Content.Shared/Nuclear14/Special/SpecialModifierSystem.cs new file mode 100644 index 00000000000..ca8fde90824 --- /dev/null +++ b/Content.Shared/Nuclear14/Special/SpecialModifierSystem.cs @@ -0,0 +1,173 @@ +using Content.Shared.Inventory; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; +using Robust.Shared.Timing; +using Robust.Shared.Utility; +using Content.Shared.DoAfter; +using Content.Shared.Nuclear14.Special.Components; + +namespace Content.Shared.Nuclear14.Special +{ + public sealed class SpecialModifierSystem : EntitySystem + { + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnGetState); + SubscribeLocalEvent(OnHandleState); + } + + private void OnGetState(EntityUid uid, SpecialComponent component, ref ComponentGetState args) + { + args.State = new SpecialModifierComponentState + { + StrengthModifier = component.StrengthModifier, + PerceptionModifier = component.PerceptionModifier, + EnduranceModifier = component.EnduranceModifier, + CharismaModifier = component.CharismaModifier, + IntelligenceModifier = component.IntelligenceModifier, + AgilityModifier = component.AgilityModifier, + LuckModifier = component.LuckModifier, + }; + } + + private void OnHandleState(EntityUid uid, SpecialComponent component, ref ComponentHandleState args) + { + if (args.Current is not SpecialModifierComponentState state) return; + component.StrengthModifier = state.StrengthModifier; + component.PerceptionModifier = state.PerceptionModifier; + component.EnduranceModifier = state.EnduranceModifier; + component.CharismaModifier = state.CharismaModifier; + component.IntelligenceModifier = state.IntelligenceModifier; + component.AgilityModifier = state.AgilityModifier; + component.LuckModifier = state.LuckModifier; + } + + public void RefreshClothingSpecialModifiers(EntityUid uid, SpecialComponent? special = null) + { + if (!Resolve(uid, ref special, false)) + return; + + if (_timing.ApplyingState) + return; + + var ev = new RefreshSpecialModifiersEvent(); + RaiseLocalEvent(uid, ev); + + if (ev.StrengthModifier == special.StrengthModifier && + ev.PerceptionModifier == special.PerceptionModifier && + ev.EnduranceModifier == special.EnduranceModifier && + ev.CharismaModifier == special.CharismaModifier && + ev.IntelligenceModifier == special.IntelligenceModifier && + ev.AgilityModifier == special.AgilityModifier && + ev.LuckModifier == special.LuckModifier + ) return; + + special.StrengthModifier = ev.StrengthModifier; + special.PerceptionModifier = ev.PerceptionModifier; + special.EnduranceModifier = ev.EnduranceModifier; + special.CharismaModifier = ev.CharismaModifier; + special.IntelligenceModifier = ev.IntelligenceModifier; + special.AgilityModifier = ev.AgilityModifier; + special.LuckModifier = ev.LuckModifier; + + var doAfterEventArgs = new DoAfterArgs(EntityManager, uid, 0, new RefreshSpecialModifiersDoAfterEvent(), uid, uid) + { + BreakOnDamage = false, + NeedHand = false, + RequireCanInteract = false, + }; + if (!_doAfter.TryStartDoAfter(doAfterEventArgs)) + return; + + Dirty(special); + } + + + [Serializable, NetSerializable] + private sealed class SpecialModifierComponentState : ComponentState + { + public int StrengthModifier; + public int PerceptionModifier; + public int EnduranceModifier; + public int CharismaModifier; + public int IntelligenceModifier; + public int AgilityModifier; + public int LuckModifier; + + } + } + + /// + /// Raised on an entity to determine its special modificators. Any system that wishes to change special modificators + /// should hook into this event and set it then. If you want this event to be raised, + /// call . + /// + public sealed class RefreshSpecialModifiersEvent : EntityEventArgs, IInventoryRelayEvent + { + public SlotFlags TargetSlots { get; } = ~SlotFlags.POCKET; + public int StrengthModifier { get; private set; } = 0; + public int PerceptionModifier { get; private set; } = 0; + public int EnduranceModifier { get; private set; } = 0; + public int CharismaModifier { get; private set; } = 0; + public int IntelligenceModifier { get; private set; } = 0; + public int AgilityModifier { get; private set; } = 0; + public int LuckModifier { get; private set; } = 0; + + public void ModifySpecial( + int strengthModifier, + int perceptionModifier, + int enduranceModifier, + int charismaModifier, + int intelligenceModifier, + int agilityModifier, + int luckModifier + ) + { + StrengthModifier += strengthModifier; + PerceptionModifier += perceptionModifier; + EnduranceModifier += enduranceModifier; + CharismaModifier += charismaModifier; + IntelligenceModifier += intelligenceModifier; + AgilityModifier += agilityModifier; + LuckModifier += luckModifier; + } + + // used to modify stats by chems + public void ModifyStrength(int strengthModifier) + { + StrengthModifier += strengthModifier; + } + public void ModifyPerception(int perceptionModifier) + { + PerceptionModifier += perceptionModifier; + } + public void ModifyEndurance(int enduranceModifier) + { + EnduranceModifier += enduranceModifier; + } + public void ModifyCharisma(int charismaModifier) + { + CharismaModifier += charismaModifier; + } + public void ModifyIntelligence(int intelligenceModifier) + { + IntelligenceModifier += intelligenceModifier; + } + public void ModifyAgility(int agilityModifier) + { + AgilityModifier += agilityModifier; + } + public void ModifyLuck(int luckModifier) + { + LuckModifier += luckModifier; + } + } + [Serializable, NetSerializable] + public sealed partial class RefreshSpecialModifiersDoAfterEvent : SimpleDoAfterEvent + { + } +} diff --git a/Content.Shared/Nuclear14/Special/SpecialPriority.cs b/Content.Shared/Nuclear14/Special/SpecialPriority.cs new file mode 100644 index 00000000000..8b7c21f607a --- /dev/null +++ b/Content.Shared/Nuclear14/Special/SpecialPriority.cs @@ -0,0 +1,20 @@ + +namespace Content.Shared.Nuclear14.Special +{ + public enum SpecialPriority + { + // These enum values HAVE to match the ones in DbSpecialPriority in Content.Server.Database + Zero = 0, + One = 1, + Two = 2, + Three = 3, + Four = 4, + Five = 5, + Six = 6, + Seven = 7, + Eight = 8, + Nine = 9, + Ten = 10 + + } +} diff --git a/Content.Shared/Nuclear14/Special/SpecialPrototype.cs b/Content.Shared/Nuclear14/Special/SpecialPrototype.cs new file mode 100644 index 00000000000..b151297f3c4 --- /dev/null +++ b/Content.Shared/Nuclear14/Special/SpecialPrototype.cs @@ -0,0 +1,51 @@ +using Content.Shared.Access; +using Content.Shared.Players.PlayTimeTracking; +using Content.Shared.StatusIcon; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; + +namespace Content.Shared.Nuclear14.Special +{ + /// + /// Describes information for a single job on the station. + /// + [Prototype("special")] + public sealed partial class SpecialPrototype : IPrototype + { + [ViewVariables] + [IdDataField] + public string ID { get; } = default!; + + /// + /// The name of this job as displayed to players. + /// + [DataField("name")] + public string Name { get; } = string.Empty; + + [ViewVariables(VVAccess.ReadOnly)] + public string LocalizedName => Loc.GetString(Name); + + /// + /// The name of this job as displayed to players. + /// + [DataField("description")] + public string? Description { get; } + + [ViewVariables(VVAccess.ReadOnly)] + public string? LocalizedDescription => Description is null ? null : Loc.GetString(Description); + + [DataField("Preference")] + public int Preference {get; set; } + + [DataField("setPreference")] + public bool SetPreference { get; } = true; + + [DataField("icon", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string Icon { get; } = "JobIconUnknown"; + + [DataField("order")] + public int Order {get; set; } + + } +} diff --git a/Content.Shared/Preferences/HumanoidCharacterProfile.cs b/Content.Shared/Preferences/HumanoidCharacterProfile.cs index 79f8fd10fde..d8ce1f8a7d6 100644 --- a/Content.Shared/Preferences/HumanoidCharacterProfile.cs +++ b/Content.Shared/Preferences/HumanoidCharacterProfile.cs @@ -14,6 +14,7 @@ using Robust.Shared.Random; using Robust.Shared.Serialization; using Robust.Shared.Utility; +using Content.Shared.Nuclear14.Special; namespace Content.Shared.Preferences { @@ -28,6 +29,7 @@ public sealed partial class HumanoidCharacterProfile : ICharacterProfile public const int MaxDescLength = 512; private readonly Dictionary _jobPriorities; + private readonly Dictionary _specialPriorities; private readonly List _antagPreferences; private readonly List _traitPreferences; private readonly List _loadoutPreferences; @@ -49,7 +51,8 @@ private HumanoidCharacterProfile( PreferenceUnavailableMode preferenceUnavailable, List antagPreferences, List traitPreferences, - List loadoutPreferences) + List loadoutPreferences, + Dictionary specialPriorities) { Name = name; FlavorText = flavortext; @@ -68,6 +71,7 @@ private HumanoidCharacterProfile( _antagPreferences = antagPreferences; _traitPreferences = traitPreferences; _loadoutPreferences = loadoutPreferences; + _specialPriorities = specialPriorities; } /// Copy constructor but with overridable references (to prevent useless copies) @@ -76,10 +80,11 @@ private HumanoidCharacterProfile( Dictionary jobPriorities, List antagPreferences, List traitPreferences, - List loadoutPreferences) + List loadoutPreferences, + Dictionary specialPriorities) : this(other.Name, other.FlavorText, other.Species, other.Height, other.Width, other.Age, other.Sex, other.Gender, other.Appearance, other.Clothing, other.Backpack, other.SpawnPriority, jobPriorities, other.PreferenceUnavailable, - antagPreferences, traitPreferences, loadoutPreferences) + antagPreferences, traitPreferences, loadoutPreferences, specialPriorities) { } @@ -87,7 +92,8 @@ private HumanoidCharacterProfile( private HumanoidCharacterProfile(HumanoidCharacterProfile other) : this(other, new Dictionary(other.JobPriorities), new List(other.AntagPreferences), new List(other.TraitPreferences), - new List(other.LoadoutPreferences)) + new List(other.LoadoutPreferences), + new Dictionary(other.SpecialPriorities)) { } @@ -108,11 +114,13 @@ public HumanoidCharacterProfile( PreferenceUnavailableMode preferenceUnavailable, IReadOnlyList antagPreferences, IReadOnlyList traitPreferences, - IReadOnlyList loadoutPreferences) + IReadOnlyList loadoutPreferences, + IReadOnlyDictionary specialPriorities) : this(name, flavortext, species, height, width, age, sex, gender, appearance, clothing, backpack, spawnPriority, new Dictionary(jobPriorities), preferenceUnavailable, new List(antagPreferences), new List(traitPreferences), - new List(loadoutPreferences)) + new List(loadoutPreferences), + new Dictionary(specialPriorities)) { } @@ -141,7 +149,18 @@ public HumanoidCharacterProfile() : this( PreferenceUnavailableMode.SpawnAsOverflow, new List(), new List(), - new List()) + new List(), + new Dictionary + { + {"Strength", SpecialPriority.Five}, + {"Perception", SpecialPriority.Five}, + {"Endurance", SpecialPriority.Five}, + {"Charisma", SpecialPriority.Five}, + {"Intelligence", SpecialPriority.Five}, + {"Agility", SpecialPriority.Five}, + {"Luck", SpecialPriority.Five} + } + ) { } @@ -172,7 +191,18 @@ public static HumanoidCharacterProfile DefaultWithSpecies(string species = Share PreferenceUnavailableMode.SpawnAsOverflow, new List(), new List(), - new List()); + new List(), + new Dictionary + { + {"Strength", SpecialPriority.Five}, + {"Perception", SpecialPriority.Five}, + {"Endurance", SpecialPriority.Five}, + {"Charisma", SpecialPriority.Five}, + {"Intelligence", SpecialPriority.Five}, + {"Agility", SpecialPriority.Five}, + {"Luck", SpecialPriority.Five} + } + ); } // TODO: This should eventually not be a visual change only. @@ -227,7 +257,17 @@ public static HumanoidCharacterProfile RandomWithSpecies(string species = Shared new Dictionary { {SharedGameTicker.FallbackOverflowJob, JobPriority.High}, - }, PreferenceUnavailableMode.StayInLobby, new List(), new List(), new List()); + }, PreferenceUnavailableMode.StayInLobby, new List(), new List(), new List(), + new Dictionary + { + {"Strength", SpecialPriority.Five}, + {"Perception", SpecialPriority.Five}, + {"Endurance", SpecialPriority.Five}, + {"Charisma", SpecialPriority.Five}, + {"Intelligence", SpecialPriority.Five}, + {"Agility", SpecialPriority.Five}, + {"Luck", SpecialPriority.Five} + }); } public string Name { get; private set; } @@ -258,6 +298,7 @@ public static HumanoidCharacterProfile RandomWithSpecies(string species = Shared public BackpackPreference Backpack { get; private set; } public SpawnPriorityPreference SpawnPriority { get; private set; } public IReadOnlyDictionary JobPriorities => _jobPriorities; + public IReadOnlyDictionary SpecialPriorities => _specialPriorities; public IReadOnlyList AntagPreferences => _antagPreferences; public IReadOnlyList TraitPreferences => _traitPreferences; public IReadOnlyList LoadoutPreferences => _loadoutPreferences; @@ -323,7 +364,11 @@ public HumanoidCharacterProfile WithSpawnPriorityPreference(SpawnPriorityPrefere public HumanoidCharacterProfile WithJobPriorities(IEnumerable> jobPriorities) { return new(this, new Dictionary(jobPriorities), _antagPreferences, _traitPreferences, - _loadoutPreferences); + _loadoutPreferences, _specialPriorities); + } + public HumanoidCharacterProfile WithSpecialPriorities(IEnumerable> specialPriorities) + { + return new(this, _jobPriorities, _antagPreferences, _traitPreferences, _loadoutPreferences, new Dictionary(specialPriorities)); } public HumanoidCharacterProfile WithJobPriority(string jobId, JobPriority priority) @@ -337,9 +382,21 @@ public HumanoidCharacterProfile WithJobPriority(string jobId, JobPriority priori { dictionary[jobId] = priority; } - return new(this, dictionary, _antagPreferences, _traitPreferences, _loadoutPreferences); + return new(this, dictionary, _antagPreferences, _traitPreferences, _loadoutPreferences, _specialPriorities); + } + public HumanoidCharacterProfile WithSpecialPriority(string specialId, SpecialPriority priority) + { + var dictionary = new Dictionary(_specialPriorities); + if (priority == SpecialPriority.Zero) + { + dictionary.Remove(specialId); + } + else + { + dictionary[specialId] = priority; + } + return new(this, _jobPriorities, _antagPreferences, _traitPreferences, _loadoutPreferences, dictionary); } - public HumanoidCharacterProfile WithPreferenceUnavailable(PreferenceUnavailableMode mode) { return new(this) { PreferenceUnavailable = mode }; @@ -348,7 +405,7 @@ public HumanoidCharacterProfile WithPreferenceUnavailable(PreferenceUnavailableM public HumanoidCharacterProfile WithAntagPreferences(IEnumerable antagPreferences) { return new(this, _jobPriorities, new List(antagPreferences), _traitPreferences, - _loadoutPreferences); + _loadoutPreferences, _specialPriorities); } public HumanoidCharacterProfile WithAntagPreference(string antagId, bool pref) @@ -368,7 +425,7 @@ public HumanoidCharacterProfile WithAntagPreference(string antagId, bool pref) list.Remove(antagId); } } - return new(this, _jobPriorities, list, _traitPreferences, _loadoutPreferences); + return new(this, _jobPriorities, list, _traitPreferences, _loadoutPreferences, _specialPriorities); } public HumanoidCharacterProfile WithTraitPreference(string traitId, bool pref) @@ -390,7 +447,7 @@ public HumanoidCharacterProfile WithTraitPreference(string traitId, bool pref) list.Remove(traitId); } } - return new(this, _jobPriorities, _antagPreferences, list, _loadoutPreferences); + return new(this, _jobPriorities, _antagPreferences, list, _loadoutPreferences, _specialPriorities); } public HumanoidCharacterProfile WithLoadoutPreference(string loadoutId, bool pref) @@ -411,7 +468,7 @@ public HumanoidCharacterProfile WithLoadoutPreference(string loadoutId, bool pre list.Remove(loadoutId); } } - return new(this, _jobPriorities, _antagPreferences, _traitPreferences, list); + return new(this, _jobPriorities, _antagPreferences, _traitPreferences, list, _specialPriorities); } public string Summary => @@ -438,7 +495,9 @@ public bool MemberwiseEquals(ICharacterProfile maybeOther) || !_jobPriorities.SequenceEqual(other._jobPriorities) || !_antagPreferences.SequenceEqual(other._antagPreferences) || !_traitPreferences.SequenceEqual(other._traitPreferences) - || !_loadoutPreferences.SequenceEqual(other._loadoutPreferences)) + || !_loadoutPreferences.SequenceEqual(other._loadoutPreferences) + || !_specialPriorities.SequenceEqual(other._specialPriorities) + ) return false; return Appearance.MemberwiseEquals(other.Appearance); } @@ -580,6 +639,23 @@ public void EnsureValid(ICommonSession session, IDependencyCollection collection _ => false })); + var specialPriorities = new Dictionary(SpecialPriorities + .Where(p => prototypeManager.HasIndex(p.Key) && p.Value switch + { + SpecialPriority.Zero => false, // Drop never since that's assumed default. + SpecialPriority.One => true, + SpecialPriority.Two => true, + SpecialPriority.Three => true, + SpecialPriority.Four => true, + SpecialPriority.Five => true, + SpecialPriority.Six => true, + SpecialPriority.Seven => true, + SpecialPriority.Eight => true, + SpecialPriority.Nine => true, + SpecialPriority.Ten => true, + _ => false + })); + var antags = AntagPreferences .Where(id => prototypeManager.TryIndex(id, out var antag) && antag.SetPreference) .ToList(); @@ -643,6 +719,13 @@ public void EnsureValid(ICommonSession session, IDependencyCollection collection _jobPriorities.Add(job, priority); } + _specialPriorities.Clear(); + + foreach (var (special, priority) in specialPriorities) + { + _specialPriorities.Add(special, priority); + } + PreferenceUnavailable = prefsUnavailableMode; _antagPreferences.Clear(); @@ -697,6 +780,9 @@ public override int GetHashCode() _antagPreferences, _traitPreferences, _loadoutPreferences + ), + HashCode.Combine( + _specialPriorities ) ); } diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index e59b4a13fed..9cb3424c6bc 100644 --- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs +++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs @@ -27,8 +27,7 @@ using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Timing; -using Robust.Shared.Toolshed.Syntax; -using ItemToggleMeleeWeaponComponent = Content.Shared.Item.ItemToggle.Components.ItemToggleMeleeWeaponComponent; +using Content.Shared.Nuclear14.Special.Components; namespace Content.Shared.Weapons.Melee; @@ -226,10 +225,37 @@ public DamageSpecifier GetDamage(EntityUid uid, EntityUid user, MeleeWeaponCompo { if (!Resolve(uid, ref component, false)) return new DamageSpecifier(); + // Nuclear14 adjust melee damage with your Strength + // 1 Strength = 0.6 + // 5 Strength = 1 + // 10 Strength = 1.5 + var damage = component.Damage; + foreach(var entry in damage.DamageDict) + { + if (entry.Value <= 0) continue; + + float newValue = entry.Value.Float(); + if (TryComp(user, out var special)){ + newValue *= 0.50f + (special.TotalStrength / 10f); + } + damage.DamageDict[entry.Key] = newValue; + } - var ev = new GetMeleeDamageEvent(uid, new (component.Damage), new(), user); + var ev = new GetMeleeDamageEvent(uid, new (damage), new(), user); RaiseLocalEvent(uid, ref ev); + // remove multiplier after dealing damage + foreach(var entry in damage.DamageDict) + { + if (entry.Value <= 0) continue; + + float newValue = entry.Value.Float(); + if (TryComp(user, out var special)){ + newValue /= 0.50f + (special.TotalStrength / 10f); + } + damage.DamageDict[entry.Key] = newValue; + } + // Nuclear14 end return DamageSpecifier.ApplyModifierSets(ev.Damage, ev.Modifiers); } diff --git a/Content.Shared/Wieldable/WieldableSystem.cs b/Content.Shared/Wieldable/WieldableSystem.cs index 6bd406c1cad..753a2de7287 100644 --- a/Content.Shared/Wieldable/WieldableSystem.cs +++ b/Content.Shared/Wieldable/WieldableSystem.cs @@ -4,6 +4,8 @@ using Content.Shared.Interaction.Events; using Content.Shared.Inventory.VirtualItem; using Content.Shared.Item; +using Content.Shared.Nuclear14.Special.Components; +using Content.Shared.Nuclear14.CCVar; using Content.Shared.Popups; using Content.Shared.Timing; using Content.Shared.Verbs; @@ -15,6 +17,7 @@ using Content.Shared.Weapons.Ranged.Systems; using Content.Shared.Wieldable.Components; using Robust.Shared.Audio.Systems; +using Robust.Shared.Configuration; using Robust.Shared.Player; namespace Content.Shared.Wieldable; @@ -22,6 +25,7 @@ namespace Content.Shared.Wieldable; public sealed class WieldableSystem : EntitySystem { [Dependency] private readonly SharedVirtualItemSystem _virtualItemSystem = default!; + [Dependency] private readonly IConfigurationManager _config = default!; [Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly SharedItemSystem _itemSystem = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; @@ -158,6 +162,23 @@ public bool CanWield(EntityUid uid, WieldableComponent component, EntityUid user return false; } + // Nuclear14 - Strength is needed to weild + if (!EntityManager.TryGetComponent(user, out var special)) + { + if(!quiet) + _popupSystem.PopupClient(Loc.GetString("player-component-no-special"), user, user); + return false; + } + + var strNeeded = _config.GetCVar(SpecialCCVars.StrengthWeild); + if(special.TotalStrength < strNeeded) + { + var message = Loc.GetString("player-component-not-enough-strength-weild", + ("number", strNeeded), ("item", uid)); + _popupSystem.PopupClient(message, user, user); + return false; + } + // Nuclear14 end // Seems legit. return true; } diff --git a/Resources/Locale/en-US/Nuclear14/Special/reagent-effects.ftl b/Resources/Locale/en-US/Nuclear14/Special/reagent-effects.ftl new file mode 100644 index 00000000000..742cb8d4ce4 --- /dev/null +++ b/Resources/Locale/en-US/Nuclear14/Special/reagent-effects.ftl @@ -0,0 +1,43 @@ +# guidebook reagent special effects + +reagent-effect-guidebook-strength-modifier = + { $chance -> + [1] Modifies + *[other] modify + } strength by {$strength} for at least {NATURALFIXED($time, 3)} {MANY("second", $time)} + +reagent-effect-guidebook-perception-modifier = + { $chance -> + [1] Modifies + *[other] modify + } perception by {$perception} for at least {NATURALFIXED($time, 3)} {MANY("second", $time)} + +reagent-effect-guidebook-endurance-modifier = + { $chance -> + [1] Modifies + *[other] modify + } endurance by {$endurance} for at least {NATURALFIXED($time, 3)} {MANY("second", $time)} + +reagent-effect-guidebook-charisma-modifier = + { $chance -> + [1] Modifies + *[other] modify + } charisma by {$charisma} for at least {NATURALFIXED($time, 3)} {MANY("second", $time)} + +reagent-effect-guidebook-intelligence-modifier = + { $chance -> + [1] Modifies + *[other] modify + } intelligence by {$intelligence} for at least {NATURALFIXED($time, 3)} {MANY("second", $time)} + +reagent-effect-guidebook-agility-modifier = + { $chance -> + [1] Modifies + *[other] modify + } agility by {$agility} for at least {NATURALFIXED($time, 3)} {MANY("second", $time)} + +reagent-effect-guidebook-luck-modifier = + { $chance -> + [1] Modifies + *[other] modify + } luck by {$luck} for at least {NATURALFIXED($time, 3)} {MANY("second", $time)} diff --git a/Resources/Locale/en-US/Nuclear14/Special/special-character-screen-description.ftl b/Resources/Locale/en-US/Nuclear14/Special/special-character-screen-description.ftl new file mode 100644 index 00000000000..30b2fad3757 --- /dev/null +++ b/Resources/Locale/en-US/Nuclear14/Special/special-character-screen-description.ftl @@ -0,0 +1,8 @@ +# Description of Special labels in character creation screen +special-character-creation-description-strength = Modifies your Melee damage +special-character-creation-description-perception = Modifies your shooting accuracy +special-character-creation-description-endurance = Modifies your HP +special-character-creation-description-charisma = Gives you accent if below 3 +special-character-creation-description-intelligence = Modifies medicine +special-character-creation-description-agility = Agility +special-character-creation-description-luck = Gives you 1% chance to pass bullet \ No newline at end of file diff --git a/Resources/Locale/en-US/Nuclear14/Special/special-character-screen.ftl b/Resources/Locale/en-US/Nuclear14/Special/special-character-screen.ftl new file mode 100644 index 00000000000..60138f1fcf4 --- /dev/null +++ b/Resources/Locale/en-US/Nuclear14/Special/special-character-screen.ftl @@ -0,0 +1,14 @@ +humanoid-profile-editor-special-priority-one-button = 1 +humanoid-profile-editor-special-priority-two-button = 2 +humanoid-profile-editor-special-priority-three-button = 3 +humanoid-profile-editor-special-priority-four-button = 4 +humanoid-profile-editor-special-priority-five-button = 5 +humanoid-profile-editor-special-priority-six-button = 6 +humanoid-profile-editor-special-priority-seven-button = 7 +humanoid-profile-editor-special-priority-eight-button = 8 +humanoid-profile-editor-special-priority-nine-button = 9 +humanoid-profile-editor-special-priority-ten-button = 10 + +humanoid-profile-editor-specials-tab = Special + +humanoid-profile-editor-special-points-label = {$points} left of {$max} points \ No newline at end of file diff --git a/Resources/Locale/en-US/Nuclear14/Special/special.ftl b/Resources/Locale/en-US/Nuclear14/Special/special.ftl new file mode 100644 index 00000000000..e0f3bd94bca --- /dev/null +++ b/Resources/Locale/en-US/Nuclear14/Special/special.ftl @@ -0,0 +1,26 @@ +# Clothing Special Modifiers examine +clothing-special-examinable-verb-text = Special Modifiers +clothing-special-examinable-verb-message = Examine clothing special modifiers. + +clothing-strength-increase-equal-examine = strength is increased by [color=green]{$strength}[/color] +clothing-strength-decrease-equal-examine = strength is decreased by [color=red]{$strength}[/color] + +clothing-perception-increase-equal-examine = perception is increased by [color=green]{$perception}[/color] +clothing-perception-decrease-equal-examine = perception is decreased by [color=red]{$perception}[/color] + +clothing-endurance-increase-equal-examine = endurance is increased by [color=green]{$endurance}[/color] +clothing-endurance-decrease-equal-examine = endurance is decreased by [color=red]{$endurance}[/color] + +clothing-charisma-increase-equal-examine = charisma is increased by [color=green]{$charisma}[/color] +clothing-charisma-decrease-equal-examine = charisma is decreased by [color=red]{$charisma}[/color] + +clothing-intelligence-increase-equal-examine = intelligence is increased by [color=green]{$intelligence}[/color] +clothing-intelligence-decrease-equal-examine = intelligence is decreased by [color=red]{$intelligence}[/color] + +clothing-agility-increase-equal-examine = agility is increased by [color=green]{$agility}[/color] +clothing-agility-decrease-equal-examine = agility is decreased by [color=red]{$agility}[/color] + +clothing-luck-increase-equal-examine = luck is increased by [color=green]{$luck}[/color] +clothing-luck-decrease-equal-examine = luck is decreased by [color=red]{$luck}[/color] + +player-component-not-enough-strength-weild = You need at least {$number} Strength to weild this diff --git a/Resources/Prototypes/Entities/Mobs/Species/human.yml b/Resources/Prototypes/Entities/Mobs/Species/human.yml index 4e174a2d8b7..3c40f142fbe 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/human.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/human.yml @@ -26,7 +26,8 @@ - GalacticCommon understands: - GalacticCommon - + - type: Special + - type: entity parent: BaseSpeciesDummy id: MobHumanDummy diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/hitscan.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/hitscan.yml index 46b9fc8edd3..2cff468f301 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/hitscan.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/hitscan.yml @@ -124,7 +124,6 @@ - type: hitscan id: RedShuttleLaser - maxLength: 60 damage: types: Heat: 45 diff --git a/Resources/Prototypes/_Nuclear14/Catalog/Fills/storage_fills_rp.yml b/Resources/Prototypes/_Nuclear14/Catalog/Fills/storage_fills_rp.yml index 89ac85126dc..e73364854cc 100644 --- a/Resources/Prototypes/_Nuclear14/Catalog/Fills/storage_fills_rp.yml +++ b/Resources/Prototypes/_Nuclear14/Catalog/Fills/storage_fills_rp.yml @@ -1866,6 +1866,17 @@ orGroup: Ammunition amount: 1 maxAmount: 2 + - id: N14PowerCellSmall + prob: 0.3 + orGroup: Ammunition + amount: 1 + maxAmount: 2 + - id: N14PowerCellAdvanced + prob: 0.1 + orGroup: Ammunition + - id: N14PowerCellFocused + prob: 0.05 + orGroup: Ammunition - id: N14PlasmaCartridge prob: 0.1 orGroup: Ammunition diff --git a/Resources/Prototypes/_Nuclear14/Catalog/Fills/storage_fills_timed.yml b/Resources/Prototypes/_Nuclear14/Catalog/Fills/storage_fills_timed.yml index 0f95a0f2051..6a6a66241ef 100644 --- a/Resources/Prototypes/_Nuclear14/Catalog/Fills/storage_fills_timed.yml +++ b/Resources/Prototypes/_Nuclear14/Catalog/Fills/storage_fills_timed.yml @@ -1866,6 +1866,17 @@ orGroup: Ammunition amount: 1 maxAmount: 2 + - id: N14PowerCellSmall + prob: 0.3 + orGroup: Ammunition + amount: 1 + maxAmount: 2 + - id: N14PowerCellAdvanced + prob: 0.1 + orGroup: Ammunition + - id: N14PowerCellFocused + prob: 0.05 + orGroup: Ammunition - id: N14PlasmaCartridge prob: 0.1 orGroup: Ammunition diff --git a/Resources/Prototypes/_Nuclear14/Entities/Clothing/Head/helmets.yml b/Resources/Prototypes/_Nuclear14/Entities/Clothing/Head/helmets.yml index a43d2dc35db..7275fc617e0 100644 --- a/Resources/Prototypes/_Nuclear14/Entities/Clothing/Head/helmets.yml +++ b/Resources/Prototypes/_Nuclear14/Entities/Clothing/Head/helmets.yml @@ -80,8 +80,8 @@ Slash: 0.8 Piercing: 0.9 Heat: 0.8 - # - type: ClothingSpecialModifier - # strengthModifier: 1 + - type: ClothingSpecialModifier + strengthModifier: 1 - type: entity parent: ClothingHeadBase diff --git a/Resources/Prototypes/_Nuclear14/Entities/Clothing/OuterClothing/falloutarmor.yml b/Resources/Prototypes/_Nuclear14/Entities/Clothing/OuterClothing/falloutarmor.yml index b914f4a74e6..2f88ac362bc 100644 --- a/Resources/Prototypes/_Nuclear14/Entities/Clothing/OuterClothing/falloutarmor.yml +++ b/Resources/Prototypes/_Nuclear14/Entities/Clothing/OuterClothing/falloutarmor.yml @@ -15,6 +15,14 @@ Slash: 0.7 Piercing: 0.9 Heat: 0.8 + - type: ClothingSpecialModifier + strengthModifier: 1 + perceptionModifier: 3 + enduranceModifier: -2 # check negative + charismaModifier: 8 # to see we don't get more than 10 + intelligenceModifier: -6 # to see we still have 1 after sum with base 5 + # agilityModifier: 1 to check it doesn't show + luckModifier: 0 # to check it does not show - type: entity parent: ClothingOuterBase diff --git a/Resources/Prototypes/_Nuclear14/Entities/Mobs/Species/ghouls.yml b/Resources/Prototypes/_Nuclear14/Entities/Mobs/Species/ghouls.yml index cc7f5f36f13..ec143dac648 100644 --- a/Resources/Prototypes/_Nuclear14/Entities/Mobs/Species/ghouls.yml +++ b/Resources/Prototypes/_Nuclear14/Entities/Mobs/Species/ghouls.yml @@ -74,12 +74,13 @@ groups: Brute: -1 Toxin: -0.1 - - type: MobThresholds + - type: MobThresholds thresholds: 0: Alive 100: Critical 150: Dead # - type: Special + - type: Special - type: entity save: false @@ -157,7 +158,7 @@ - type: Damageable damageContainer: Biological damageModifierSet: GhoulGlowing - # - type: Special + - type: Special - type: entity save: false diff --git a/Resources/Prototypes/_Nuclear14/Entities/Objects/Specific/Kits/kits.yml b/Resources/Prototypes/_Nuclear14/Entities/Objects/Specific/Kits/kits.yml index 749483f9fde..e5b2ac2fd61 100644 --- a/Resources/Prototypes/_Nuclear14/Entities/Objects/Specific/Kits/kits.yml +++ b/Resources/Prototypes/_Nuclear14/Entities/Objects/Specific/Kits/kits.yml @@ -536,5 +536,7 @@ - id: N14WeaponLaserPistol - id: N14PowerCellHigh amount: 3 + - id: N14PowerCellSmall + amount: 2 - id: N14Stimpak - id: BoxMRE diff --git a/Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Ammunition/Battery/Battery.yml b/Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Ammunition/Battery/Battery.yml index d487975a8f9..840a115f69c 100644 --- a/Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Ammunition/Battery/Battery.yml +++ b/Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Ammunition/Battery/Battery.yml @@ -1,6 +1,6 @@ - type: entity name: microfusion cell - description: A small energy container the size of a palm, commonly used has ammo for any kind of laser weapon. + description: A microfusion cell, typically used as ammunition for large energy weapons. id: N14BasePowerCell abstract: true parent: BaseItem @@ -37,6 +37,8 @@ proto: RedMediumLaser fireCost: 50 +#MARK: MF + - type: entity name: microfusion cell description: A small energy container the size of a palm, commonly used has ammo for any kind of laser weapon. @@ -56,6 +58,94 @@ maxCharge: 1200 startingCharge: 1200 +#MARK: MF Focused + +- type: entity + name: heavy microfusion cell + description: A microfusion cell, typically used as ammunition for large energy weapons. Modified to expand a lot more charge at once to deal considerably more destruction per shot. + id: N14PowerCellFocused + suffix: Full + parent: N14BasePowerCell + components: + - type: Sprite + sprite: _Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/microfusionfocused.rsi + layers: + - map: [ "enum.PowerCellVisualLayers.Base" ] + state: empty + - map: [ "enum.PowerCellVisualLayers.Unshaded" ] + state: o2 + shader: unshaded + - type: Tag + tags: + - N14PowerCellFocused + - N14AmmoCell + - type: Battery + maxCharge: 1200 + startingCharge: 1200 + - type: HitscanBatteryAmmoProvider + proto: RedShuttleLaser + fireCost: 75 + +#MARK: EC + +- type: entity + name: energy cell + description: An energy cell, typically used as ammunition for small-arms energy weapons. + id: N14PowerCellSmall + suffix: Full + parent: N14BasePowerCell + components: + - type: Sprite + sprite: _Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/EC.rsi + layers: + - map: [ "enum.PowerCellVisualLayers.Base" ] + state: empty + scale: 0.6, 0.6 + - map: [ "enum.PowerCellVisualLayers.Unshaded" ] + state: o2 + shader: unshaded + scale: 0.6, 0.6 + - type: Tag + tags: + - N14PowerCellSmall + - N14AmmoCell + - type: Battery + maxCharge: 600 + startingCharge: 600 + - type: HitscanBatteryAmmoProvider + proto: RedLaser + fireCost: 50 + +#MARK: ECP + +- type: entity + name: electron charge pack + description: An electron charge pack, typically used as ammunition for advanced energy weapons. + id: N14PowerCellAdvanced + suffix: Full + parent: N14BasePowerCell + components: + - type: Sprite + sprite: _Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/ECP.rsi + layers: + - map: [ "enum.PowerCellVisualLayers.Base" ] + state: empty + - map: [ "enum.PowerCellVisualLayers.Unshaded" ] + state: o2 + shader: unshaded + - type: Tag + tags: + - N14PowerCellAdvanced + - N14AmmoCell + - type: Battery + maxCharge: 2375 + startingCharge: 2375 + - type: HitscanBatteryAmmoProvider + proto: RedHeavyLaser + fireCost: 125 + +#MARK: Fusion Cell + - type: entity name: fusion cell description: A large energy container the size of a hand, commonly used as ammo for heavy laser weapons. @@ -84,6 +174,8 @@ proto: RedLaser fireCost: 50 +#MARK: Plasma cartridge + - type: entity name: plasma cartridge description: A small green glowing cartridge, commonly used has ammo for any kind of plasma weapon. @@ -142,6 +234,8 @@ maxCharge: 1200 startingCharge: 1200 +#MARK: Plasma shell + - type: entity name: plasma shell description: A big green glowing shell, commonly used has ammo for heavy plasma weapons. diff --git a/Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml b/Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml index 422d819f3f2..08df22d5123 100644 --- a/Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml +++ b/Resources/Prototypes/_Nuclear14/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml @@ -65,10 +65,10 @@ - Belt # MARK: Pistols - type: entity - name: laser pistol + name: AEP-7 laser pistol parent: N14BaseWeaponPowerCellSmall id: N14WeaponLaserPistol - description: A highly advanced laser pistol. + description: A basic energy-based laser gun that fires concentrated beams of light. components: - type: Sprite sprite: _Nuclear14/Objects/Weapons/Guns/Battery/laser_pistol.rsi @@ -88,14 +88,14 @@ slots: gun_magazine: name: Magazine - startingItem: N14PowerCellHigh + startingItem: N14PowerCellSmall insertSound: /Audio/Weapons/Guns/MagIn/batrifle_magin.ogg ejectSound: /Audio/Weapons/Guns/MagOut/batrifle_magout.ogg whitelist: tags: - - N14PowerCellHigh + - N14PowerCellSmall - type: Gun - fireRate: 1 + fireRate: 1.5 - type: entity name: plasma pistol @@ -167,8 +167,8 @@ path: /Audio/Weapons/plasma_cutter.ogg fireRate: 2.5 -# MARK: Recharger guns - +# MARK: Recharger + - type: entity name: recharger rifle parent: BaseWeaponBattery @@ -187,7 +187,7 @@ sprite: _Nuclear14/Objects/Weapons/Guns/Battery/recharger_rifle.rsi - type: BatterySelfRecharger autoRecharge: true - autoRechargeRate: 50 + autoRechargeRate: 25 - type: Gun selectedMode: SemiAuto availableModes: @@ -220,7 +220,7 @@ fireCost: 50 - type: BatterySelfRecharger autoRecharge: true - autoRechargeRate: 40 + autoRechargeRate: 20 - type: MagazineVisuals magState: mag steps: 5 @@ -230,13 +230,13 @@ tags: - Sidearm -# MARK: Fullauto Rifles +# MARK: Fullauto - type: entity - name: laser automatic rifle + name: Modified AER9 laser rifle parent: N14BaseWeaponPowerCellMedium id: N14WeaponLaserAutoRifle - description: A highly advanced automatic laser rifle. + description: A sturdy pre-war laser rifle. Emits beams of concentrated light to kill targets. This one has been jury-rigged against common sense with a rotating barrel to fire in full auto. components: - type: Wieldable - type: GunWieldBonus @@ -322,20 +322,18 @@ availableModes: - FullAuto -# MARK: Semiauto Rifles - - type: entity - name: laser rifle + name: prototype AER14 laser rifle parent: N14BaseWeaponPowerCellMedium - id: N14WeaponLaserRifle - description: A highly advanced laser rifle. + id: N14WeaponLaserProtoRifle + description: A bleeding-edge, pre-war laser rifle. Extremely powerful, but eats ECPs like nothing else. components: - type: Wieldable - type: GunWieldBonus minAngle: -34 maxAngle: -63 - type: Sprite - sprite: _Nuclear14/Objects/Weapons/Guns/Battery/laser_rifle.rsi + sprite: _Nuclear14/Objects/Weapons/Guns/Battery/laser_rifle_proto.rsi layers: - state: base map: ["enum.GunVisualLayers.Base"] @@ -343,7 +341,7 @@ map: ["enum.GunVisualLayers.MagUnshaded"] shader: unshaded - type: Clothing - sprite: _Nuclear14/Objects/Weapons/Guns/Battery/laser_rifle.rsi + sprite: _Nuclear14/Objects/Weapons/Guns/Battery/laser_rifle_proto.rsi quickEquip: false slots: - Back @@ -356,30 +354,32 @@ slots: gun_magazine: name: Magazine - startingItem: N14PowerCellHigh + startingItem: N14PowerCellAdvanced insertSound: /Audio/Weapons/Guns/MagIn/batrifle_magin.ogg ejectSound: /Audio/Weapons/Guns/MagOut/batrifle_magout.ogg whitelist: tags: - - N14PowerCellHigh + - N14PowerCellAdvanced - type: Gun - fireRate: 3 - selectedMode: SemiAuto + fireRate: 4 + selectedMode: FullAuto availableModes: - - SemiAuto + - FullAuto + +# MARK: Semiauto - type: entity - name: prototype laser rifle + name: AER-9 parent: N14BaseWeaponPowerCellMedium - id: N14WeaponLaserProtoRifle - description: The last and most advanced model of laser rifle ever made. + id: N14WeaponLaserRifle + description: A sturdy pre-war laser rifle. Emits beams of concentrated light to kill targets. components: - type: Wieldable - type: GunWieldBonus minAngle: -34 maxAngle: -63 - type: Sprite - sprite: _Nuclear14/Objects/Weapons/Guns/Battery/laser_rifle_proto.rsi + sprite: _Nuclear14/Objects/Weapons/Guns/Battery/laser_rifle.rsi layers: - state: base map: ["enum.GunVisualLayers.Base"] @@ -387,7 +387,7 @@ map: ["enum.GunVisualLayers.MagUnshaded"] shader: unshaded - type: Clothing - sprite: _Nuclear14/Objects/Weapons/Guns/Battery/laser_rifle_proto.rsi + sprite: _Nuclear14/Objects/Weapons/Guns/Battery/laser_rifle.rsi quickEquip: false slots: - Back @@ -407,7 +407,7 @@ tags: - N14PowerCellHigh - type: Gun - fireRate: 4 + fireRate: 3 selectedMode: SemiAuto availableModes: - SemiAuto @@ -505,6 +505,7 @@ - SemiAuto # MARK: Shotguns + - type: entity name: multiplas parent: N14BaseWeaponPowerCellMedium @@ -548,13 +549,14 @@ soundGunshot: path: /Audio/Weapons/plasma_cutter.ogg -#MARK: Sniper (just a different looking laser rifle until we get more options) +#MARK: Focused +# (just a different looking laser rifle until we get more options) - type: entity name: wattz 3000 laser rifle parent: N14BaseWeaponPowerCellMedium id: N14WeaponWattz3000 - description: A highly advanced laser rifle. + description: Wattz 3000 Laser Rifle. Uses focused micro fusion cells for more powerful lasers, and an extended barrel for additional range. components: - type: Wieldable - type: GunWieldBonus @@ -582,12 +584,12 @@ slots: gun_magazine: name: Magazine - startingItem: N14PowerCellHigh + startingItem: N14PowerCellFocused insertSound: /Audio/Weapons/Guns/MagIn/batrifle_magin.ogg ejectSound: /Audio/Weapons/Guns/MagOut/batrifle_magout.ogg whitelist: tags: - - N14PowerCellHigh + - N14PowerCellFocused - type: Gun fireRate: 3 selectedMode: SemiAuto @@ -598,7 +600,7 @@ name: wattz 2000 laser rifle parent: N14BaseWeaponPowerCellMedium id: N14WeaponWattz2000 - description: A highly advanced laser rifle. + description: Wattz 2000 Laser Rifle. Uses focused micro fusion cells for more powerful lasers, and an extended barrel for additional range. components: - type: Wieldable - type: GunWieldBonus @@ -626,12 +628,12 @@ slots: gun_magazine: name: Magazine - startingItem: N14PowerCellHigh + startingItem: N14PowerCellFocused insertSound: /Audio/Weapons/Guns/MagIn/batrifle_magin.ogg ejectSound: /Audio/Weapons/Guns/MagOut/batrifle_magout.ogg whitelist: tags: - - N14PowerCellHigh + - N14PowerCellFocused - type: Gun fireRate: 2 selectedMode: SemiAuto diff --git a/Resources/Prototypes/_Nuclear14/Reagents/chems.yml b/Resources/Prototypes/_Nuclear14/Reagents/chems.yml index c96df3a43f0..88eefdff103 100644 --- a/Resources/Prototypes/_Nuclear14/Reagents/chems.yml +++ b/Resources/Prototypes/_Nuclear14/Reagents/chems.yml @@ -23,6 +23,22 @@ # agilityModifier: 1 # - !type:LuckModifierReagent # Nuclear 14 Example of adding modifier to Chemicals # luckModifier: 1 + - !type:StrengthModifierReagent + - !type:StrengthModifierReagent # Nuclear 14 Example of adding modifier to Chemicals + strengthModifier: 1 + # statusLifetime: 5 # may be useful if we want to make it last longer after reagent is gone from blood? + - !type:PerceptionModifierReagent # Nuclear 14 Example of adding modifier to Chemicals + perceptionModifier: 1 + - !type:EnduranceModifierReagent # Nuclear 14 Example of adding modifier to Chemicals + enduranceModifier: 1 + - !type:CharismaModifierReagent # Nuclear 14 Example of adding modifier to Chemicals + charismaModifier: 1 + - !type:IntelligenceModifierReagent # Nuclear 14 Example of adding modifier to Chemicals + intelligenceModifier: 1 + - !type:AgilityModifierReagent # Nuclear 14 Example of adding modifier to Chemicals + agilityModifier: 1 + - !type:LuckModifierReagent # Nuclear 14 Example of adding modifier to Chemicals + luckModifier: 1 - !type:MovespeedModifier walkSpeedModifier: 1.15 sprintSpeedModifier: 1.15 diff --git a/Resources/Prototypes/_Nuclear14/Special/specials.yml b/Resources/Prototypes/_Nuclear14/Special/specials.yml index 12363a906ee..54a1b3c0924 100644 --- a/Resources/Prototypes/_Nuclear14/Special/specials.yml +++ b/Resources/Prototypes/_Nuclear14/Special/specials.yml @@ -1,49 +1,49 @@ # Special prototypes used in Character creation screen -# - type: special - # id: Strength - # name: Strength - # description: special-character-creation-description-strength - # icon: "SpecialIconStrength" - # order: 0 +- type: special + id: Strength + name: Strength + description: special-character-creation-description-strength + icon: "SpecialIconStrength" + order: 0 -# - type: special - # id: Perception - # name: Perception - # description: special-character-creation-description-perception - # icon: "SpecialIconPerception" - # order: 1 +- type: special + id: Perception + name: Perception + description: special-character-creation-description-perception + icon: "SpecialIconPerception" + order: 1 -# - type: special - # id: Endurance - # name: Endurance - # description: special-character-creation-description-endurance - # icon: "SpecialIconEndurance" - # order: 2 +- type: special + id: Endurance + name: Endurance + description: special-character-creation-description-endurance + icon: "SpecialIconEndurance" + order: 2 -# - type: special - # id: Charisma - # name: Charisma - # description: special-character-creation-description-charisma - # icon: "SpecialIconCharisma" - # order: 3 +- type: special + id: Charisma + name: Charisma + description: special-character-creation-description-charisma + icon: "SpecialIconCharisma" + order: 3 -# - type: special - # id: Intelligence - # name: Intelligence - # description: special-character-creation-description-intelligence - # icon: "SpecialIconIntelligence" - # order: 4 +- type: special + id: Intelligence + name: Intelligence + description: special-character-creation-description-intelligence + icon: "SpecialIconIntelligence" + order: 4 -# - type: special - # id: Agility - # name: Agility - # description: special-character-creation-description-agility - # icon: "SpecialIconAgility" - # order: 5 +- type: special + id: Agility + name: Agility + description: special-character-creation-description-agility + icon: "SpecialIconAgility" + order: 5 -# - type: special - # id: Luck - # name: Luck - # description: special-character-creation-description-luck - # icon: "SpecialIconLuck" - # order: 6 +- type: special + id: Luck + name: Luck + description: special-character-creation-description-luck + icon: "SpecialIconLuck" + order: 6 diff --git a/Resources/Prototypes/_Nuclear14/tags.yml b/Resources/Prototypes/_Nuclear14/tags.yml index 8e6d65bae69..8111d1e1a71 100644 --- a/Resources/Prototypes/_Nuclear14/tags.yml +++ b/Resources/Prototypes/_Nuclear14/tags.yml @@ -164,6 +164,15 @@ - type: Tag id: N14PlasmaShell +- type: Tag + id: N14PowerCellSmall + +- type: Tag + id: N14PowerCellFocused + +- type: Tag + id: N14PowerCellAdvanced + # Ammo tags(explosives) - type: Tag diff --git a/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/EC.rsi/empty.png b/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/EC.rsi/empty.png new file mode 100644 index 00000000000..f5e89ce0a8c Binary files /dev/null and b/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/EC.rsi/empty.png differ diff --git a/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/EC.rsi/meta.json b/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/EC.rsi/meta.json new file mode 100644 index 00000000000..13bd1e33ebd --- /dev/null +++ b/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/EC.rsi/meta.json @@ -0,0 +1,26 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "taken from BigIron github", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "empty" + }, + { + "name": "o2" + }, + { + "name": "o3" + }, + { + "name": "pref-o2" + }, + { + "name": "o1" + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/EC.rsi/o1.png b/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/EC.rsi/o1.png new file mode 100644 index 00000000000..c895c5bc64b Binary files /dev/null and b/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/EC.rsi/o1.png differ diff --git a/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/EC.rsi/o2.png b/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/EC.rsi/o2.png new file mode 100644 index 00000000000..5dbf370f1a1 Binary files /dev/null and b/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/EC.rsi/o2.png differ diff --git a/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/EC.rsi/o3.png b/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/EC.rsi/o3.png new file mode 100644 index 00000000000..d98e6dfc1b6 Binary files /dev/null and b/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/EC.rsi/o3.png differ diff --git a/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/EC.rsi/pref-o2.png b/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/EC.rsi/pref-o2.png new file mode 100644 index 00000000000..e575cde49db Binary files /dev/null and b/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/EC.rsi/pref-o2.png differ diff --git a/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/ECP.rsi/empty.png b/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/ECP.rsi/empty.png new file mode 100644 index 00000000000..2bfdde8f7c8 Binary files /dev/null and b/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/ECP.rsi/empty.png differ diff --git a/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/ECP.rsi/meta.json b/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/ECP.rsi/meta.json new file mode 100644 index 00000000000..240adeea20e --- /dev/null +++ b/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/ECP.rsi/meta.json @@ -0,0 +1,26 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "taken from BigIron github", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "empty" + }, + { + "name": "o2" + }, + { + "name": "o1", + "delays": [ + [ + 0.8, + 0.8 + ] + ] + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/ECP.rsi/o1.png b/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/ECP.rsi/o1.png new file mode 100644 index 00000000000..2eaed6fde2c Binary files /dev/null and b/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/ECP.rsi/o1.png differ diff --git a/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/ECP.rsi/o2.png b/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/ECP.rsi/o2.png new file mode 100644 index 00000000000..32c721d921d Binary files /dev/null and b/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/ECP.rsi/o2.png differ diff --git a/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/microfusionfocused.rsi/empty.png b/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/microfusionfocused.rsi/empty.png new file mode 100644 index 00000000000..a6245bbf5f3 Binary files /dev/null and b/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/microfusionfocused.rsi/empty.png differ diff --git a/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/microfusionfocused.rsi/meta.json b/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/microfusionfocused.rsi/meta.json new file mode 100644 index 00000000000..240adeea20e --- /dev/null +++ b/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/microfusionfocused.rsi/meta.json @@ -0,0 +1,26 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "taken from BigIron github", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "empty" + }, + { + "name": "o2" + }, + { + "name": "o1", + "delays": [ + [ + 0.8, + 0.8 + ] + ] + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/microfusionfocused.rsi/o1.png b/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/microfusionfocused.rsi/o1.png new file mode 100644 index 00000000000..2da604f3abb Binary files /dev/null and b/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/microfusionfocused.rsi/o1.png differ diff --git a/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/microfusionfocused.rsi/o2.png b/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/microfusionfocused.rsi/o2.png new file mode 100644 index 00000000000..73693da98a8 Binary files /dev/null and b/Resources/Textures/_Nuclear14/Objects/Weapons/Guns/Ammunition/Battery/microfusionfocused.rsi/o2.png differ