diff --git a/Content.Client/CartridgeLoader/Cartridges/MailMetricUi.cs b/Content.Client/CartridgeLoader/Cartridges/MailMetricUi.cs new file mode 100644 index 0000000000..504dda7c56 --- /dev/null +++ b/Content.Client/CartridgeLoader/Cartridges/MailMetricUi.cs @@ -0,0 +1,28 @@ +using Content.Client.UserInterface.Fragments; +using Content.Shared.CartridgeLoader.Cartridges; +using Robust.Client.UserInterface; + +namespace Content.Client.CartridgeLoader.Cartridges; + +public sealed partial class MailMetricUi : UIFragment +{ + private MailMetricUiFragment? _fragment; + + public override Control GetUIFragmentRoot() + { + return _fragment!; + } + + public override void Setup(BoundUserInterface userInterface, EntityUid? fragmentOwner) + { + _fragment = new MailMetricUiFragment(); + } + + public override void UpdateState(BoundUserInterfaceState state) + { + if (state is MailMetricUiState cast) + { + _fragment?.UpdateState(cast); + } + } +} diff --git a/Content.Client/CartridgeLoader/Cartridges/MailMetricUiFragment.xaml b/Content.Client/CartridgeLoader/Cartridges/MailMetricUiFragment.xaml new file mode 100644 index 0000000000..39639fe8c9 --- /dev/null +++ b/Content.Client/CartridgeLoader/Cartridges/MailMetricUiFragment.xaml @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Content.Client/CartridgeLoader/Cartridges/MailMetricUiFragment.xaml.cs b/Content.Client/CartridgeLoader/Cartridges/MailMetricUiFragment.xaml.cs new file mode 100644 index 0000000000..553e3a5793 --- /dev/null +++ b/Content.Client/CartridgeLoader/Cartridges/MailMetricUiFragment.xaml.cs @@ -0,0 +1,104 @@ +using Content.Shared.CartridgeLoader.Cartridges; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; + +namespace Content.Client.CartridgeLoader.Cartridges; + +[GenerateTypedNameReferences] +public sealed partial class MailMetricUiFragment : BoxContainer +{ + + private OpenedMailPercentGrade? _successGrade; + + public MailMetricUiFragment() + { + RobustXamlLoader.Load(this); + + // This my way of adding multiple classes to a XAML control. + // Haha Batman I'm going to blow up Gotham City + OpenedMailCount.StyleClasses.Add("Good"); + OpenedMailSpesos.StyleClasses.Add("Good"); + TamperedMailCount.StyleClasses.Add("Danger"); + TamperedMailSpesos.StyleClasses.Add("Danger"); + ExpiredMailCount.StyleClasses.Add("Danger"); + ExpiredMailSpesos.StyleClasses.Add("Danger"); + DamagedMailCount.StyleClasses.Add("Danger"); + DamagedMailSpesos.StyleClasses.Add("Danger"); + UnopenedMailCount.StyleClasses.Add("Caution"); + } + + public void UpdateState(MailMetricUiState state) + { + UpdateTextLabels(state); + UpdateSuccessGrade(state); + } + + public void UpdateTextLabels(MailMetricUiState state) + { + var stats = state.Metrics; + + OpenedMailCount.Text = stats.OpenedCount.ToString(); + OpenedMailSpesos.Text = stats.Earnings.ToString(); + TamperedMailCount.Text = stats.TamperedCount.ToString(); + TamperedMailSpesos.Text = stats.TamperedLosses.ToString(); + ExpiredMailCount.Text = stats.ExpiredCount.ToString(); + ExpiredMailSpesos.Text = stats.ExpiredLosses.ToString(); + DamagedMailCount.Text = stats.DamagedCount.ToString(); + DamagedMailSpesos.Text = stats.DamagedLosses.ToString(); + UnopenedMailCount.Text = state.UnopenedMailCount.ToString(); + TotalMailCount.Text = state.TotalMail.ToString(); + TotalMailSpesos.Text = stats.TotalIncome.ToString(); + SuccessRateCounts.Text = Loc.GetString("mail-metrics-progress", + ("opened", stats.OpenedCount), + ("total", state.TotalMail)); + SuccessRatePercent.Text = Loc.GetString("mail-metrics-progress-percent", + ("successRate", state.SuccessRate)); + } + + public void UpdateSuccessGrade(MailMetricUiState state) + { + var previousGrade = _successGrade; + _successGrade = GetSuccessRateGrade(state.SuccessRate); + + // No need to update if they're the same + if (previousGrade == _successGrade) + return; + + var previousGradeClass = GetClassForGrade(previousGrade); + if (previousGradeClass != string.Empty) + { + SuccessRatePercent.StyleClasses.Remove(previousGradeClass); + } + + SuccessRatePercent.StyleClasses.Add(GetClassForGrade(_successGrade)); + } + + private static OpenedMailPercentGrade GetSuccessRateGrade(double successRate) + { + return successRate switch + { + > 75 => OpenedMailPercentGrade.Good, + > 50 => OpenedMailPercentGrade.Average, + _ => OpenedMailPercentGrade.Bad, + }; + } + + private string GetClassForGrade(OpenedMailPercentGrade? grade) + { + return grade switch + { + OpenedMailPercentGrade.Good => "Good", + OpenedMailPercentGrade.Average => "Caution", + OpenedMailPercentGrade.Bad => "Danger", + _ => string.Empty, + }; + } +} + +enum OpenedMailPercentGrade +{ + Good, + Average, + Bad +} diff --git a/Content.Client/Input/ContentContexts.cs b/Content.Client/Input/ContentContexts.cs index 0e56153752..c54f5002ec 100644 --- a/Content.Client/Input/ContentContexts.cs +++ b/Content.Client/Input/ContentContexts.cs @@ -74,6 +74,7 @@ public static void SetupContexts(IInputContextContainer contexts) human.AddFunction(ContentKeyFunctions.OpenBelt); human.AddFunction(ContentKeyFunctions.OfferItem); human.AddFunction(ContentKeyFunctions.ToggleStanding); + human.AddFunction(ContentKeyFunctions.ToggleCrawlingUnder); human.AddFunction(ContentKeyFunctions.MouseMiddle); human.AddFunction(ContentKeyFunctions.ArcadeUp); human.AddFunction(ContentKeyFunctions.ArcadeDown); diff --git a/Content.Client/Options/UI/Tabs/KeyRebindTab.xaml.cs b/Content.Client/Options/UI/Tabs/KeyRebindTab.xaml.cs index ab4ebd83fa..f84c20b7ed 100644 --- a/Content.Client/Options/UI/Tabs/KeyRebindTab.xaml.cs +++ b/Content.Client/Options/UI/Tabs/KeyRebindTab.xaml.cs @@ -102,7 +102,7 @@ private void HandleHoldLookUp(BaseButton.ButtonToggledEventArgs args) _cfg.SetCVar(CCVars.HoldLookUp, args.Pressed); _cfg.SaveToFile(); } - + private void HandleDefaultWalk(BaseButton.ButtonToggledEventArgs args) { _cfg.SetCVar(CCVars.DefaultWalk, args.Pressed); @@ -205,6 +205,7 @@ void AddCheckBox(string checkBoxName, bool currentState, Action(OnMovementInput); - SubscribeNetworkEvent(OnDowned); - SubscribeNetworkEvent(OnStood); - SubscribeNetworkEvent(OnCheckAutoGetUp); } + public override void Update(float frameTime) + { + // Update draw depth of laying down entities as necessary + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var layingDown, out var standing, out var sprite)) + { + // Do not modify the entities draw depth if it's modified externally + if (sprite.DrawDepth != layingDown.NormalDrawDepth && sprite.DrawDepth != layingDown.CrawlingUnderDrawDepth) + continue; + + sprite.DrawDepth = standing.CurrentState is StandingState.Lying && layingDown.IsCrawlingUnder + ? layingDown.CrawlingUnderDrawDepth + : layingDown.NormalDrawDepth; + } + + query.Dispose(); + } + private void OnMovementInput(EntityUid uid, LayingDownComponent component, MoveEvent args) { if (!_timing.IsFirstTimePredicted @@ -51,26 +66,6 @@ private void OnMovementInput(EntityUid uid, LayingDownComponent component, MoveE sprite.Rotation = Angle.FromDegrees(90); } - private void OnDowned(DrawDownedEvent args) - { - var uid = GetEntity(args.Uid); - if (!TryComp(uid, out var sprite) - || !TryComp(uid, out var component)) - return; - - sprite.DrawDepth = component.CrawlingDrawDepth; - } - - private void OnStood(DrawStoodEvent args) - { - var uid = GetEntity(args.Uid); - if (!TryComp(uid, out var sprite) - || !TryComp(uid, out var component)) - return; - - sprite.DrawDepth = component.NormalDrawDepth; - } - private void OnCheckAutoGetUp(CheckAutoGetUpEvent ev, EntitySessionEventArgs args) { if (!_timing.IsFirstTimePredicted) diff --git a/Content.Server.Database/Migrations/Postgres/20241001054803_CustomSpecieName.cs b/Content.Server.Database/Migrations/Postgres/20241001054803_CustomSpecieName.cs index ecbbb46eb5..6c40d6240f 100644 --- a/Content.Server.Database/Migrations/Postgres/20241001054803_CustomSpecieName.cs +++ b/Content.Server.Database/Migrations/Postgres/20241001054803_CustomSpecieName.cs @@ -10,27 +10,20 @@ public partial class CustomSpecieName : Migration /// protected override void Up(MigrationBuilder migrationBuilder) { - migrationBuilder.AlterColumn( + migrationBuilder.AddColumn( name: "custom_specie_name", table: "profile", type: "text", nullable: false, - defaultValue: "", - oldClrType: typeof(string), - oldType: "text", - oldNullable: true); + defaultValue: ""); } /// protected override void Down(MigrationBuilder migrationBuilder) { - migrationBuilder.AlterColumn( + migrationBuilder.DropColumn( name: "custom_specie_name", - table: "profile", - type: "text", - nullable: true, - oldClrType: typeof(string), - oldType: "text"); + table: "profile"); } } -} +} \ No newline at end of file diff --git a/Content.Server.Database/Migrations/Sqlite/20241001054735_CustomSpecieName.cs b/Content.Server.Database/Migrations/Sqlite/20241001054735_CustomSpecieName.cs index d48c7f87d9..a1e968045b 100644 --- a/Content.Server.Database/Migrations/Sqlite/20241001054735_CustomSpecieName.cs +++ b/Content.Server.Database/Migrations/Sqlite/20241001054735_CustomSpecieName.cs @@ -10,27 +10,20 @@ public partial class CustomSpecieName : Migration /// protected override void Up(MigrationBuilder migrationBuilder) { - migrationBuilder.AlterColumn( + migrationBuilder.AddColumn( name: "custom_specie_name", table: "profile", type: "TEXT", nullable: false, - defaultValue: "", - oldClrType: typeof(string), - oldType: "TEXT", - oldNullable: true); + defaultValue: ""); } /// protected override void Down(MigrationBuilder migrationBuilder) { - migrationBuilder.AlterColumn( + migrationBuilder.DropColumn( name: "custom_specie_name", - table: "profile", - type: "TEXT", - nullable: true, - oldClrType: typeof(string), - oldType: "TEXT"); + table: "profile"); } } } diff --git a/Content.Server/Antag/AntagSelectionSystem.API.cs b/Content.Server/Antag/AntagSelectionSystem.API.cs index 59bf05fe03..77f543cdcf 100644 --- a/Content.Server/Antag/AntagSelectionSystem.API.cs +++ b/Content.Server/Antag/AntagSelectionSystem.API.cs @@ -5,6 +5,7 @@ using Content.Server.Objectives; using Content.Shared.Chat; using Content.Shared.Mind; +using Content.Shared.Preferences; using JetBrains.Annotations; using Robust.Shared.Audio; using Robust.Shared.Enums; @@ -156,6 +157,36 @@ public List GetAntagMindEntityUids(Entity e return ent.Comp.SelectedMinds.Select(p => p.Item1).ToList(); } + /// + /// Checks if a given session has the primary antag preferences for a given definition + /// + public bool HasPrimaryAntagPreference(ICommonSession? session, AntagSelectionDefinition def) + { + if (session == null) + return true; + + if (def.PrefRoles.Count == 0) + return false; + + var pref = (HumanoidCharacterProfile) _pref.GetPreferences(session.UserId).SelectedCharacter; + return pref.AntagPreferences.Any(p => def.PrefRoles.Contains(p)); + } + + /// + /// Checks if a given session has the fallback antag preferences for a given definition + /// + public bool HasFallbackAntagPreference(ICommonSession? session, AntagSelectionDefinition def) + { + if (session == null) + return true; + + if (def.FallbackRoles.Count == 0) + return false; + + var pref = (HumanoidCharacterProfile) _pref.GetPreferences(session.UserId).SelectedCharacter; + return pref.AntagPreferences.Any(p => def.FallbackRoles.Contains(p)); + } + /// /// Returns all the antagonists for this rule who are currently alive /// diff --git a/Content.Server/Antag/AntagSelectionSystem.cs b/Content.Server/Antag/AntagSelectionSystem.cs index d74824dd2d..cd4d836e68 100644 --- a/Content.Server/Antag/AntagSelectionSystem.cs +++ b/Content.Server/Antag/AntagSelectionSystem.cs @@ -17,7 +17,6 @@ using Content.Shared.Ghost; using Content.Shared.Humanoid; using Content.Shared.Players; -using Content.Shared.Preferences; using Robust.Server.Audio; using Robust.Server.GameObjects; using Robust.Server.Player; @@ -118,12 +117,15 @@ private void OnSpawnComplete(PlayerSpawnCompleteEvent args) // something to figure out later. var query = QueryActiveRules(); + var rules = new List<(EntityUid, AntagSelectionComponent)>(); while (query.MoveNext(out var uid, out _, out var antag, out _)) { - // TODO ANTAG - // what why aasdiuhasdopiuasdfhksad - // stop this insanity please - // probability of antag assignment shouldn't depend on the order in which rules are returned by the query. + rules.Add((uid, antag)); + } + RobustRandom.Shuffle(rules); + + foreach (var (uid, antag) in rules) + { if (!RobustRandom.Prob(LateJoinRandomChance)) continue; @@ -221,13 +223,13 @@ public void ChooseAntags(Entity ent, IList /// Tries to makes a given player into the specified antagonist. /// - public bool TryMakeAntag(Entity ent, ICommonSession? session, AntagSelectionDefinition def, bool ignoreSpawner = false) + public bool TryMakeAntag(Entity ent, ICommonSession? session, AntagSelectionDefinition def, bool ignoreSpawner = false, bool checkPref = true) { - if (!IsSessionValid(ent, session, def) || - !IsEntityValid(session?.AttachedEntity, def)) - { + if (checkPref && !HasPrimaryAntagPreference(session, def)) + return false; + + if (!IsSessionValid(ent, session, def) || !IsEntityValid(session?.AttachedEntity, def)) return false; - } MakeAntag(ent, session, def, ignoreSpawner); return true; @@ -324,16 +326,14 @@ public AntagSelectionPlayerPool GetPlayerPool(Entity en var fallbackList = new List(); foreach (var session in sessions) { - if (!IsSessionValid(ent, session, def) || - !IsEntityValid(session.AttachedEntity, def)) + if (!IsSessionValid(ent, session, def) || !IsEntityValid(session.AttachedEntity, def)) continue; - var pref = (HumanoidCharacterProfile) _pref.GetPreferences(session.UserId).SelectedCharacter; - if (def.PrefRoles.Count != 0 && pref.AntagPreferences.Any(p => def.PrefRoles.Contains(p))) + if (HasPrimaryAntagPreference(session, def)) { preferredList.Add(session); } - else if (def.FallbackRoles.Count != 0 && pref.AntagPreferences.Any(p => def.FallbackRoles.Contains(p))) + else if (HasFallbackAntagPreference(session, def)) { fallbackList.Add(session); } @@ -404,13 +404,13 @@ public bool IsEntityValid(EntityUid? entity, AntagSelectionDefinition def) if (def.Whitelist != null) { - if (!def.Whitelist.IsValid(entity.Value, EntityManager)) + if (!def.Whitelist.IsValid(entity.Value)) return false; } if (def.Blacklist != null) { - if (def.Blacklist.IsValid(entity.Value, EntityManager)) + if (def.Blacklist.IsValid(entity.Value)) return false; } diff --git a/Content.Server/Cargo/Components/StationLogisticStatsDatabaseComponent.cs b/Content.Server/Cargo/Components/StationLogisticStatsDatabaseComponent.cs new file mode 100644 index 0000000000..ca1fbeaad0 --- /dev/null +++ b/Content.Server/Cargo/Components/StationLogisticStatsDatabaseComponent.cs @@ -0,0 +1,14 @@ +using Content.Shared.Cargo; +using Content.Shared.CartridgeLoader.Cartridges; + +namespace Content.Server.Cargo.Components; + +/// +/// Added to the abstract representation of a station to track stats related to mail delivery and income +/// +[RegisterComponent, Access(typeof(SharedCargoSystem))] +public sealed partial class StationLogisticStatsComponent : Component +{ + [DataField] + public MailStats Metrics { get; set; } +} diff --git a/Content.Server/Cargo/Systems/LogisticStatsSystem.cs b/Content.Server/Cargo/Systems/LogisticStatsSystem.cs new file mode 100644 index 0000000000..6abf4eb5a4 --- /dev/null +++ b/Content.Server/Cargo/Systems/LogisticStatsSystem.cs @@ -0,0 +1,63 @@ +using Content.Shared.Cargo; +using Content.Server.Cargo.Components; +using JetBrains.Annotations; + +namespace Content.Server.Cargo.Systems; + +public sealed partial class LogisticStatsSystem : SharedCargoSystem +{ + [PublicAPI] + public void AddOpenedMailEarnings(EntityUid uid, StationLogisticStatsComponent component, int earnedMoney) + { + component.Metrics = component.Metrics with + { + Earnings = component.Metrics.Earnings + earnedMoney, + OpenedCount = component.Metrics.OpenedCount + 1 + }; + UpdateLogisticsStats(uid); + } + + [PublicAPI] + public void AddExpiredMailLosses(EntityUid uid, StationLogisticStatsComponent component, int lostMoney) + { + component.Metrics = component.Metrics with + { + ExpiredLosses = component.Metrics.ExpiredLosses + lostMoney, + ExpiredCount = component.Metrics.ExpiredCount + 1 + }; + UpdateLogisticsStats(uid); + } + + [PublicAPI] + public void AddDamagedMailLosses(EntityUid uid, StationLogisticStatsComponent component, int lostMoney) + { + component.Metrics = component.Metrics with + { + DamagedLosses = component.Metrics.DamagedLosses + lostMoney, + DamagedCount = component.Metrics.DamagedCount + 1 + }; + UpdateLogisticsStats(uid); + } + + [PublicAPI] + public void AddTamperedMailLosses(EntityUid uid, StationLogisticStatsComponent component, int lostMoney) + { + component.Metrics = component.Metrics with + { + TamperedLosses = component.Metrics.TamperedLosses + lostMoney, + TamperedCount = component.Metrics.TamperedCount + 1 + }; + UpdateLogisticsStats(uid); + } + + private void UpdateLogisticsStats(EntityUid uid) => RaiseLocalEvent(new LogisticStatsUpdatedEvent(uid)); +} + +public sealed class LogisticStatsUpdatedEvent : EntityEventArgs +{ + public EntityUid Station; + public LogisticStatsUpdatedEvent(EntityUid station) + { + Station = station; + } +} diff --git a/Content.Server/CartridgeLoader/Cartridges/MailMetricsCartridgeComponent.cs b/Content.Server/CartridgeLoader/Cartridges/MailMetricsCartridgeComponent.cs new file mode 100644 index 0000000000..380a4d90c0 --- /dev/null +++ b/Content.Server/CartridgeLoader/Cartridges/MailMetricsCartridgeComponent.cs @@ -0,0 +1,11 @@ +namespace Content.Server.CartridgeLoader.Cartridges; + +[RegisterComponent, Access(typeof(MailMetricsCartridgeSystem))] +public sealed partial class MailMetricsCartridgeComponent : Component +{ + /// + /// Station entity keeping track of logistics stats + /// + [DataField] + public EntityUid? Station; +} diff --git a/Content.Server/CartridgeLoader/Cartridges/MailMetricsCartridgeSystem.cs b/Content.Server/CartridgeLoader/Cartridges/MailMetricsCartridgeSystem.cs new file mode 100644 index 0000000000..00b6d0a16e --- /dev/null +++ b/Content.Server/CartridgeLoader/Cartridges/MailMetricsCartridgeSystem.cs @@ -0,0 +1,82 @@ +using Content.Server.Cargo.Components; +using Content.Server.Cargo.Systems; +using Content.Server.Mail.Components; +using Content.Server.Station.Systems; +using Content.Shared.CartridgeLoader; +using Content.Shared.CartridgeLoader.Cartridges; + +namespace Content.Server.CartridgeLoader.Cartridges; + +public sealed class MailMetricsCartridgeSystem : EntitySystem +{ + [Dependency] private readonly CartridgeLoaderSystem _cartridgeLoader = default!; + [Dependency] private readonly StationSystem _station = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnUiReady); + SubscribeLocalEvent(OnLogisticsStatsUpdated); + SubscribeLocalEvent(OnMapInit); + } + + private void OnUiReady(Entity ent, ref CartridgeUiReadyEvent args) + { + UpdateUI(ent, args.Loader); + } + + private void OnLogisticsStatsUpdated(LogisticStatsUpdatedEvent args) + { + UpdateAllCartridges(args.Station); + } + + private void OnMapInit(EntityUid uid, MailComponent mail, MapInitEvent args) + { + if (_station.GetOwningStation(uid) is { } station) + UpdateAllCartridges(station); + } + + private void UpdateAllCartridges(EntityUid station) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var comp, out var cartridge)) + { + if (cartridge.LoaderUid is not { } loader || comp.Station != station) + continue; + UpdateUI((uid, comp), loader); + } + } + + private void UpdateUI(Entity ent, EntityUid loader) + { + if (_station.GetOwningStation(loader) is { } station) + ent.Comp.Station = station; + + if (!TryComp(ent.Comp.Station, out var logiStats)) + return; + + // Get station's logistic stats + var unopenedMailCount = GetUnopenedMailCount(ent.Comp.Station); + + // Send logistic stats to cartridge client + var state = new MailMetricUiState(logiStats.Metrics, unopenedMailCount); + _cartridgeLoader.UpdateCartridgeUiState(loader, state); + } + + + private int GetUnopenedMailCount(EntityUid? station) + { + var unopenedMail = 0; + + var query = EntityQueryEnumerator(); + + while (query.MoveNext(out var uid, out var comp)) + { + if (comp.IsLocked && _station.GetOwningStation(uid) == station) + unopenedMail++; + } + + return unopenedMail; + } +} diff --git a/Content.Server/Mail/Components/DelayedItemComponent.cs b/Content.Server/Mail/Components/DelayedItemComponent.cs new file mode 100644 index 0000000000..f73aef85b0 --- /dev/null +++ b/Content.Server/Mail/Components/DelayedItemComponent.cs @@ -0,0 +1,15 @@ +namespace Content.Server.Mail.Components; + +/// +/// A placeholder for another entity, spawned when dropped or placed in someone's hands. +/// Useful for storing instant effect entities, e.g. smoke, in the mail. +/// +[RegisterComponent] +public sealed partial class DelayedItemComponent : Component +{ + /// + /// The entity to replace this when opened or dropped. + /// + [DataField] + public string Item = "None"; +} diff --git a/Content.Server/Mail/Components/MailComponent.cs b/Content.Server/Mail/Components/MailComponent.cs new file mode 100644 index 0000000000..af0bdaa87f --- /dev/null +++ b/Content.Server/Mail/Components/MailComponent.cs @@ -0,0 +1,111 @@ +using System.Threading; +using Robust.Shared.Audio; +using Content.Shared.Storage; +using Content.Shared.Mail; + +namespace Content.Server.Mail.Components; + +[RegisterComponent] +public sealed partial class MailComponent : SharedMailComponent +{ + [DataField] + public string Recipient = "None"; + + [DataField] + public string RecipientJob = "None"; + + /// + /// Why do we not use LockComponent? + /// Because this can't be locked again, + /// and we have special conditions for unlocking, + /// and we don't want to add a verb. + /// + [DataField] + public bool IsLocked = true; + + /// + /// Is this parcel profitable to deliver for the station? + /// + /// + /// The station won't receive any award on delivery if this is false. + /// This is useful for broken fragile packages and packages that were + /// not delivered in time. + /// + [DataField] + public bool IsProfitable = true; + + /// + /// Is this package considered fragile? + /// + /// + /// This can be set to true in the YAML files for a mail delivery to + /// always be Fragile, despite its contents. + /// + [DataField] + public bool IsFragile = false; + + /// + /// Is this package considered priority mail? + /// + /// + /// There will be a timer set for its successful delivery. The + /// station's bank account will be penalized if it is not delivered on + /// time.
+ ///
+ /// This is set to false on successful delivery.
+ ///
+ /// This can be set to true in the YAML files for a mail delivery to always be Priority. + ///
+ [DataField] + public bool IsPriority = false; + + /// + /// Whether this parcel is large. + /// + [DataField] + public bool IsLarge = false; + + /// + /// What will be packaged when the mail is spawned. + /// + [DataField] + public List Contents = new(); + + /// + /// The amount that cargo will be awarded for delivering this mail. + /// + [DataField] + public int Bounty = 750; + + /// + /// Penalty if the mail is destroyed. + /// + [DataField] + public int Penalty = -250; + + /// + /// The sound that's played when the mail's lock is broken. + /// + [DataField] + public SoundSpecifier PenaltySound = new SoundPathSpecifier("/Audio/Machines/Nuke/angry_beep.ogg"); + + /// + /// The sound that's played when the mail's opened. + /// + [DataField] + public SoundSpecifier OpenSound = new SoundPathSpecifier("/Audio/Effects/packetrip.ogg"); + + /// + /// The sound that's played when the mail's lock has been emagged. + /// + [DataField] + public SoundSpecifier EmagSound = new SoundCollectionSpecifier("sparks"); + + /// + /// Whether this component is enabled. + /// Removed when it becomes trash. + /// + public bool IsEnabled = true; + + public CancellationTokenSource? PriorityCancelToken; +} diff --git a/Content.Server/Mail/Components/MailReceiverComponent.cs b/Content.Server/Mail/Components/MailReceiverComponent.cs new file mode 100644 index 0000000000..281a27ea79 --- /dev/null +++ b/Content.Server/Mail/Components/MailReceiverComponent.cs @@ -0,0 +1,4 @@ +namespace Content.Server.Mail.Components; + +[RegisterComponent] +public sealed partial class MailReceiverComponent : Component {} diff --git a/Content.Server/Mail/Components/MailTeleporterComponent.cs b/Content.Server/Mail/Components/MailTeleporterComponent.cs new file mode 100644 index 0000000000..81f58bc7a5 --- /dev/null +++ b/Content.Server/Mail/Components/MailTeleporterComponent.cs @@ -0,0 +1,118 @@ +using Robust.Shared.Audio; + +namespace Content.Server.Mail.Components; + +/// +/// This is for the mail teleporter. +/// Random mail will be teleported to this every few minutes. +/// +[RegisterComponent] +public sealed partial class MailTeleporterComponent : Component +{ + // Not starting accumulator at 0 so mail carriers have some deliveries to make shortly after roundstart. + [DataField] + public float Accumulator = 285f; + + [DataField] + public TimeSpan TeleportInterval = TimeSpan.FromMinutes(5); + + /// + /// The sound that's played when new mail arrives. + /// + [DataField] + public SoundSpecifier TeleportSound = new SoundPathSpecifier("/Audio/Effects/teleport_arrival.ogg"); + + /// + /// The MailDeliveryPoolPrototype that's used to select what mail this + /// teleporter can deliver. + /// + [DataField] + public string MailPool = "RandomDeltaVMailDeliveryPool"; + + /// + /// How many mail candidates do we need per actual delivery sent when + /// the mail goes out? The number of candidates is divided by this number + /// to determine how many deliveries will be teleported in. + /// It does not determine unique recipients. That is random. + /// + [DataField] + public int CandidatesPerDelivery = 8; + + [DataField] + public int MinimumDeliveriesPerTeleport = 1; + + /// + /// Do not teleport any more mail in, if there are at least this many + /// undelivered parcels. + /// + /// + /// Currently this works by checking how many MailComponent entities + /// are sitting on the teleporter's tile.

+ /// + /// It should be noted that if the number of actual deliveries to be + /// made based on the number of candidates divided by candidates per + /// delivery exceeds this number, the teleporter will spawn more mail + /// than this number.

+ /// + /// This is just a simple check to see if anyone's been picking up the + /// mail lately to prevent entity bloat for the sake of performance. + ///
+ [DataField] + public int MaximumUndeliveredParcels = 5; + + /// + /// Any item that breaks or is destroyed in less than this amount of + /// damage is one of the types of items considered fragile. + /// + [DataField] + public int FragileDamageThreshold = 10; + + /// + /// What's the bonus for delivering a fragile package intact? + /// + [DataField] + public int FragileBonus = 100; + + /// + /// What's the malus for failing to deliver a fragile package? + /// + [DataField] + public int FragileMalus = -100; + + /// + /// What's the chance for any one delivery to be marked as priority mail? + /// + [DataField] + public float PriorityChance = 0.1f; + + /// + /// How long until a priority delivery is considered as having failed + /// if not delivered? + /// + [DataField] + public TimeSpan PriorityDuration = TimeSpan.FromMinutes(5); + + /// + /// What's the bonus for delivering a priority package on time? + /// + [DataField] + public int PriorityBonus = 250; + + /// + /// What's the malus for failing to deliver a priority package? + /// + [DataField] + public int PriorityMalus = -250; + + /// + /// What's the bonus for delivering a large package intact? + /// + [DataField] + public int LargeBonus = 1500; + + /// + /// What's the malus for failing to deliver a large package? + /// + [DataField] + public int LargeMalus = -500; +} diff --git a/Content.Server/Nyanotrasen/Mail/Components/StationMailRouterComponent.cs b/Content.Server/Mail/Components/StationMailRouterComponent.cs similarity index 51% rename from Content.Server/Nyanotrasen/Mail/Components/StationMailRouterComponent.cs rename to Content.Server/Mail/Components/StationMailRouterComponent.cs index ce87eb131f..6f6df1e10b 100644 --- a/Content.Server/Nyanotrasen/Mail/Components/StationMailRouterComponent.cs +++ b/Content.Server/Mail/Components/StationMailRouterComponent.cs @@ -1,7 +1,7 @@ -namespace Content.Server.Mail; +namespace Content.Server.Mail.Components; /// -/// Designates a station as a place for sending and receiving mail. +/// Designates a station as a place for sending and receiving mail. /// [RegisterComponent] public sealed partial class StationMailRouterComponent : Component diff --git a/Content.Server/Nyanotrasen/Mail/MailCommands.cs b/Content.Server/Mail/MailCommands.cs similarity index 69% rename from Content.Server/Nyanotrasen/Mail/MailCommands.cs rename to Content.Server/Mail/MailCommands.cs index 5af873f9e8..390de93fb3 100644 --- a/Content.Server/Nyanotrasen/Mail/MailCommands.cs +++ b/Content.Server/Mail/MailCommands.cs @@ -5,6 +5,7 @@ using Content.Shared.Administration; using Content.Server.Administration; using Content.Server.Mail.Components; +using Content.Server.Mail.Systems; namespace Content.Server.Mail; @@ -20,6 +21,7 @@ public sealed class MailToCommand : IConsoleCommand [Dependency] private readonly IEntitySystemManager _entitySystemManager = default!; private readonly string _blankMailPrototype = "MailAdminFun"; + private readonly string _blankLargeMailPrototype = "MailLargeAdminFun"; // Frontier: large mail private readonly string _container = "storagebase"; private readonly string _mailContainer = "contents"; @@ -44,21 +46,23 @@ public async void Execute(IConsoleShell shell, string argStr, string[] args) return; } - if (!Boolean.TryParse(args[2], out var isFragile)) + if (!bool.TryParse(args[2], out var isFragile) || !bool.TryParse(args[3], out var isPriority)) { shell.WriteError(Loc.GetString("shell-invalid-bool")); return; } - if (!Boolean.TryParse(args[3], out var isPriority)) + var isLarge = false; + if (args.Length > 4 && !bool.TryParse(args[4], out isLarge)) { shell.WriteError(Loc.GetString("shell-invalid-bool")); return; } + var mailPrototype = isLarge ? _blankLargeMailPrototype : _blankMailPrototype; - var _mailSystem = _entitySystemManager.GetEntitySystem(); - var _containerSystem = _entitySystemManager.GetEntitySystem(); + var mailSystem = _entitySystemManager.GetEntitySystem(); + var containerSystem = _entitySystemManager.GetEntitySystem(); if (!_entityManager.TryGetComponent(recipientUid, out MailReceiverComponent? mailReceiver)) { @@ -66,49 +70,50 @@ public async void Execute(IConsoleShell shell, string argStr, string[] args) return; } - if (!_prototypeManager.HasIndex(_blankMailPrototype)) + if (!_prototypeManager.HasIndex(mailPrototype)) { - shell.WriteLine(Loc.GetString("command-mailto-no-blankmail", ("blankMail", _blankMailPrototype))); + shell.WriteLine(Loc.GetString("command-mailto-no-blankmail", ("blankMail", mailPrototype))); return; } - if (!_containerSystem.TryGetContainer(containerUid, _container, out var targetContainer)) + if (!containerSystem.TryGetContainer(containerUid, _container, out var targetContainer)) { shell.WriteLine(Loc.GetString("command-mailto-invalid-container", ("requiredContainer", _container))); return; } - if (!_mailSystem.TryGetMailRecipientForReceiver(mailReceiver, out MailRecipient? recipient)) + if (!mailSystem.TryGetMailRecipientForReceiver(mailReceiver, out MailRecipient? recipient)) { shell.WriteLine(Loc.GetString("command-mailto-unable-to-receive")); return; } - if (!_mailSystem.TryGetMailTeleporterForReceiver(mailReceiver, out MailTeleporterComponent? teleporterComponent)) + if (!mailSystem.TryGetMailTeleporterForReceiver(mailReceiver, out MailTeleporterComponent? teleporterComponent)) { shell.WriteLine(Loc.GetString("command-mailto-no-teleporter-found")); return; } - var mailUid = _entityManager.SpawnEntity(_blankMailPrototype, _entityManager.GetComponent(containerUid).Coordinates); - var mailContents = _containerSystem.EnsureContainer(mailUid, _mailContainer); + var mailUid = _entityManager.SpawnEntity(mailPrototype, _entityManager.GetComponent(containerUid).Coordinates); + var mailContents = containerSystem.EnsureContainer(mailUid, _mailContainer); if (!_entityManager.TryGetComponent(mailUid, out MailComponent? mailComponent)) { - shell.WriteLine(Loc.GetString("command-mailto-bogus-mail", ("blankMail", _blankMailPrototype), ("requiredMailComponent", nameof(MailComponent)))); + shell.WriteLine(Loc.GetString("command-mailto-bogus-mail", ("blankMail", mailPrototype), ("requiredMailComponent", nameof(MailComponent)))); return; } foreach (var entity in targetContainer.ContainedEntities.ToArray()) - _containerSystem.Insert(entity, mailContents); + containerSystem.Insert(entity, mailContents); mailComponent.IsFragile = isFragile; mailComponent.IsPriority = isPriority; + mailComponent.IsLarge = isLarge; - _mailSystem.SetupMail(mailUid, teleporterComponent, recipient.Value); + mailSystem.SetupMail(mailUid, teleporterComponent, recipient.Value); - var teleporterQueue = _containerSystem.EnsureContainer(teleporterComponent.Owner, "queued"); - _containerSystem.Insert(mailUid, teleporterQueue); + var teleporterQueue = containerSystem.EnsureContainer(teleporterComponent.Owner, "queued"); + containerSystem.Insert(mailUid, teleporterQueue); shell.WriteLine(Loc.GetString("command-mailto-success", ("timeToTeleport", teleporterComponent.TeleportInterval.TotalSeconds - teleporterComponent.Accumulator))); } } @@ -125,7 +130,7 @@ public sealed class MailNowCommand : IConsoleCommand public async void Execute(IConsoleShell shell, string argStr, string[] args) { - var _mailSystem = _entitySystemManager.GetEntitySystem(); + var entitySystem = _entitySystemManager.GetEntitySystem(); foreach (var mailTeleporter in _entityManager.EntityQuery()) { diff --git a/Content.Server/Mail/MailConstants.cs b/Content.Server/Mail/MailConstants.cs new file mode 100644 index 0000000000..38d37a1326 --- /dev/null +++ b/Content.Server/Mail/MailConstants.cs @@ -0,0 +1,37 @@ +namespace Content.Server.Mail; + +/// +/// A set of localized strings related to mail entities +/// +public struct MailEntityStrings +{ + public string NameAddressed; + public string DescClose; + public string DescFar; +} + +/// +/// Constants related to mail. +/// +public static class MailConstants +{ + /// + /// Locale strings related to small parcels. + /// + public static readonly MailEntityStrings Mail = new() + { + NameAddressed = "mail-item-name-addressed", + DescClose = "mail-desc-close", + DescFar = "mail-desc-far" + }; + + /// + /// Locale strings related to large packages. + /// + public static readonly MailEntityStrings MailLarge = new() + { + NameAddressed = "mail-large-item-name-addressed", + DescClose = "mail-large-desc-close", + DescFar = "mail-large-desc-far" + }; +} diff --git a/Content.Server/Mail/Systems/DelayedItemSystem.cs b/Content.Server/Mail/Systems/DelayedItemSystem.cs new file mode 100644 index 0000000000..59aaa3aff6 --- /dev/null +++ b/Content.Server/Mail/Systems/DelayedItemSystem.cs @@ -0,0 +1,43 @@ +using Content.Shared.Damage; +using Content.Shared.Hands; +using Robust.Shared.Containers; + +namespace Content.Server.Mail.Systems; + +/// +/// A placeholder for another entity, spawned when taken out of a container, with the placeholder deleted shortly after. +/// Useful for storing instant effect entities, e.g. smoke, in the mail. +/// +public sealed class DelayedItemSystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnDropAttempt); + SubscribeLocalEvent(OnHandEquipped); + SubscribeLocalEvent(OnDamageChanged); + SubscribeLocalEvent(OnRemovedFromContainer); + } + + private void OnRemovedFromContainer(EntityUid uid, Components.DelayedItemComponent component, ContainerModifiedMessage args) + { + Spawn(component.Item, Transform(uid).Coordinates); + } + + private void OnHandEquipped(EntityUid uid, Components.DelayedItemComponent component, EquippedHandEvent args) + { + EntityManager.DeleteEntity(uid); + } + + private void OnDropAttempt(EntityUid uid, Components.DelayedItemComponent component, DropAttemptEvent args) + { + EntityManager.DeleteEntity(uid); + } + + private void OnDamageChanged(EntityUid uid, Components.DelayedItemComponent component, DamageChangedEvent args) + { + Spawn(component.Item, Transform(uid).Coordinates); + EntityManager.DeleteEntity(uid); + } +} diff --git a/Content.Server/Mail/Systems/MailSystem.cs b/Content.Server/Mail/Systems/MailSystem.cs new file mode 100644 index 0000000000..e80febd230 --- /dev/null +++ b/Content.Server/Mail/Systems/MailSystem.cs @@ -0,0 +1,756 @@ +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading; +using Content.Server.Access.Systems; +using Content.Server.Cargo.Components; +using Content.Server.Cargo.Systems; +using Content.Server.Chat.Systems; +using Content.Server.Chemistry.Containers.EntitySystems; +using Content.Server.Damage.Components; +using Content.Server.Destructible; +using Content.Server.Destructible.Thresholds; +using Content.Server.Destructible.Thresholds.Behaviors; +using Content.Server.Destructible.Thresholds.Triggers; +using Content.Server.Item; +using Content.Server.Mail.Components; +using Content.Server.Mind; +using Content.Server.Popups; +using Content.Server.Power.Components; +using Content.Server.Spawners.EntitySystems; +using Content.Server.Station.Systems; +using Content.Shared.Access; +using Content.Shared.Access.Components; +using Content.Shared.Access.Systems; +using Content.Shared.Chat; +using Content.Shared.Damage; +using Content.Shared.Destructible; +using Content.Shared.Emag.Components; +using Content.Shared.Emag.Systems; +using Content.Shared.Examine; +using Content.Shared.Fluids.Components; +using Content.Shared.Hands.EntitySystems; +using Content.Shared.Interaction; +using Content.Shared.Interaction.Events; +using Content.Shared.Mail; +using Content.Shared.Maps; +using Content.Shared.Nutrition.Components; +using Content.Shared.Nutrition.EntitySystems; +using Content.Shared.PDA; +using Content.Shared.Roles; +using Content.Shared.Storage; +using Content.Shared.Tag; +using Robust.Shared.Audio; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Containers; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using Timer = Robust.Shared.Timing.Timer; + +namespace Content.Server.Mail.Systems; + +public sealed class MailSystem : EntitySystem +{ + [Dependency] private readonly PopupSystem _popupSystem = default!; + [Dependency] private readonly AccessReaderSystem _accessSystem = default!; + [Dependency] private readonly SharedHandsSystem _handsSystem = default!; + [Dependency] private readonly IdCardSystem _idCardSystem = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly TagSystem _tagSystem = default!; + [Dependency] private readonly CargoSystem _cargoSystem = default!; + [Dependency] private readonly StationSystem _stationSystem = default!; + [Dependency] private readonly ChatSystem _chatSystem = default!; + [Dependency] private readonly OpenableSystem _openable = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly SharedContainerSystem _containerSystem = default!; + [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; + [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; + [Dependency] private readonly SharedAudioSystem _audioSystem = default!; + [Dependency] private readonly DamageableSystem _damageableSystem = default!; + [Dependency] private readonly MindSystem _mindSystem = default!; + [Dependency] private readonly MetaDataSystem _metaDataSystem = default!; + + // DeltaV - system that keeps track of mail and cargo stats + [Dependency] private readonly LogisticStatsSystem _logisticsStatsSystem = default!; + + private ISawmill _sawmill = default!; + + public override void Initialize() + { + base.Initialize(); + + _sawmill = Logger.GetSawmill("mail"); + + SubscribeLocalEvent(OnSpawnPlayer, after: new[] { typeof(SpawnPointSystem) }); + + SubscribeLocalEvent(OnRemove); + SubscribeLocalEvent(OnUseInHand); + SubscribeLocalEvent(OnAfterInteractUsing); + SubscribeLocalEvent(OnExamined); + SubscribeLocalEvent(OnDestruction); + SubscribeLocalEvent(OnDamage); + SubscribeLocalEvent(OnBreak); + SubscribeLocalEvent(OnMailEmagged); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + foreach (var mailTeleporter in EntityQuery()) + { + if (TryComp(mailTeleporter.Owner, out var power) && !power.Powered) + continue; + + mailTeleporter.Accumulator += frameTime; + if (mailTeleporter.Accumulator < mailTeleporter.TeleportInterval.TotalSeconds) + continue; + + mailTeleporter.Accumulator -= (float) mailTeleporter.TeleportInterval.TotalSeconds; + + SpawnMail(mailTeleporter.Owner, mailTeleporter); + } + } + + /// + /// Dynamically add the MailReceiver component to appropriate entities. + /// + private void OnSpawnPlayer(PlayerSpawningEvent args) + { + if (args.SpawnResult == null + || args.Job == null + || args.Station is not {} station + || !HasComp(station)) + return; + + AddComp(args.SpawnResult.Value); + } + + private void OnRemove(EntityUid uid, MailComponent component, ComponentRemove args) + { + // Make sure the priority timer doesn't run. + if (component.PriorityCancelToken != null) + component.PriorityCancelToken.Cancel(); + } + + /// + /// Try to open the mail. + /// + private void OnUseInHand(EntityUid uid, MailComponent component, UseInHandEvent args) + { + if (!component.IsEnabled) + return; + if (component.IsLocked) + { + _popupSystem.PopupEntity(Loc.GetString("mail-locked"), uid, args.User); + return; + } + OpenMail(uid, component, args.User); + } + + /// + /// Handle logic similar between a normal mail unlock and an emag + /// frying out the lock. + /// + private void UnlockMail(EntityUid uid, MailComponent component) + { + component.IsLocked = false; + UpdateAntiTamperVisuals(uid, false); + + if (component.IsPriority) + { + // This is a successful delivery. Keep the failure timer from triggering. + if (component.PriorityCancelToken != null) + component.PriorityCancelToken.Cancel(); + + // The priority tape is visually considered to be a part of the + // anti-tamper lock, so remove that too. + _appearanceSystem.SetData(uid, MailVisuals.IsPriority, false); + + // The examination code depends on this being false to not show + // the priority tape description anymore. + component.IsPriority = false; + } + } + + /// + /// Check the ID against the mail's lock + /// + private void OnAfterInteractUsing(EntityUid uid, MailComponent component, AfterInteractUsingEvent args) + { + if (!args.CanReach || !component.IsLocked || !TryComp(uid, out var access)) + return; + + IdCardComponent? idCard = null; // We need an ID card. + if (HasComp(args.Used)) // Can we find it in a PDA if the user is using that? + { + _idCardSystem.TryGetIdCard(args.Used, out var pdaID); + idCard = pdaID; + } + + if (HasComp(args.Used)) // Or are they using an id card directly? + idCard = Comp(args.Used); + + if (idCard == null) // Return if we still haven't found an id card. + return; + + if (!HasComp(uid)) + { + if (idCard.FullName != component.Recipient || idCard.JobTitle != component.RecipientJob) + { + _popupSystem.PopupEntity(Loc.GetString("mail-recipient-mismatch"), uid, args.User); + return; + } + + if (!_accessSystem.IsAllowed(uid, args.User)) + { + _popupSystem.PopupEntity(Loc.GetString("mail-invalid-access"), uid, args.User); + return; + } + } + + ExecuteForEachLogisticsStats(uid, (station, logisticStats) => + { + _logisticsStatsSystem.AddOpenedMailEarnings(station, + logisticStats, + component.IsProfitable ? component.Bounty : 0); + }); + UnlockMail(uid, component); + + if (!component.IsProfitable) + { + _popupSystem.PopupEntity(Loc.GetString("mail-unlocked"), uid, args.User); + return; + } + + _popupSystem.PopupEntity(Loc.GetString("mail-unlocked-reward", ("bounty", component.Bounty)), uid, args.User); + + component.IsProfitable = false; + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var station, out var account)) + { + if (_stationSystem.GetOwningStation(uid) != station) + continue; + + _cargoSystem.UpdateBankAccount(station, account, component.Bounty); + } + } + + private void OnExamined(EntityUid uid, MailComponent component, ExaminedEvent args) + { + MailEntityStrings mailEntityStrings = component.IsLarge ? MailConstants.MailLarge : MailConstants.Mail; //Frontier: mail types stored per type (large mail) + if (!args.IsInDetailsRange) + { + args.PushMarkup(Loc.GetString(mailEntityStrings.DescFar)); // Frontier: mail constants struct + return; + } + + args.PushMarkup(Loc.GetString(mailEntityStrings.DescClose, ("name", component.Recipient), ("job", component.RecipientJob))); // Frontier: mail constants struct + + if (component.IsFragile) + args.PushMarkup(Loc.GetString("mail-desc-fragile")); + + if (component.IsPriority) + { + if (component.IsProfitable) + args.PushMarkup(Loc.GetString("mail-desc-priority")); + else + args.PushMarkup(Loc.GetString("mail-desc-priority-inactive")); + } + } + + /// + /// Penalize a station for a failed delivery. + /// + /// + /// This will mark a parcel as no longer being profitable, which will + /// prevent multiple failures on different conditions for the same + /// delivery.

+ /// + /// The standard penalization is breaking the anti-tamper lock, + /// but this allows a delivery to fail for other reasons too + /// while having a generic function to handle different messages. + ///
+ public void PenalizeStationFailedDelivery(EntityUid uid, MailComponent component, string localizationString) + { + if (!component.IsProfitable) + return; + + _chatSystem.TrySendInGameICMessage(uid, Loc.GetString(localizationString, ("credits", component.Penalty)), InGameICChatType.Speak, false); + _audioSystem.PlayPvs(component.PenaltySound, uid); + + component.IsProfitable = false; + + if (component.IsPriority) + _appearanceSystem.SetData(uid, MailVisuals.IsPriorityInactive, true); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var station, out var account)) + { + if (_stationSystem.GetOwningStation(uid) != station) + continue; + + _cargoSystem.UpdateBankAccount(station, account, component.Penalty); + return; + } + } + + private void OnDestruction(EntityUid uid, MailComponent component, DestructionEventArgs args) + { + if (component.IsLocked) + { + // DeltaV - Tampered mail recorded to logistic stats + ExecuteForEachLogisticsStats(uid, (station, logisticStats) => + { + _logisticsStatsSystem.AddTamperedMailLosses(station, + logisticStats, + component.IsProfitable ? component.Penalty : 0); + }); + + PenalizeStationFailedDelivery(uid, component, "mail-penalty-lock"); + } + + if (component.IsEnabled) + OpenMail(uid, component); + + UpdateAntiTamperVisuals(uid, false); + } + + private void OnDamage(EntityUid uid, MailComponent component, DamageChangedEvent args) + { + if (args.DamageDelta == null || !_containerSystem.TryGetContainer(uid, "contents", out var contents)) + return; + + // Transfer damage to the contents. + // This should be a general-purpose feature for all containers in the future. + foreach (var entity in contents.ContainedEntities.ToArray()) + _damageableSystem.TryChangeDamage(entity, args.DamageDelta); + } + + private void OnBreak(EntityUid uid, MailComponent component, BreakageEventArgs args) + { + _appearanceSystem.SetData(uid, MailVisuals.IsBroken, true); + + if (component.IsFragile) + { + // DeltaV - Broken mail recorded to logistic stats + ExecuteForEachLogisticsStats(uid, (station, logisticStats) => + { + _logisticsStatsSystem.AddDamagedMailLosses(station, + logisticStats, + component.IsProfitable ? component.Penalty : 0); + }); + + PenalizeStationFailedDelivery(uid, component, "mail-penalty-fragile"); + } + } + + private void OnMailEmagged(EntityUid uid, MailComponent component, ref GotEmaggedEvent args) + { + if (!component.IsLocked) + return; + + UnlockMail(uid, component); + + _popupSystem.PopupEntity(Loc.GetString("mail-unlocked-by-emag"), uid, args.UserUid); + + _audioSystem.PlayPvs(component.EmagSound, uid, AudioParams.Default.WithVolume(4)); + component.IsProfitable = false; + args.Handled = true; + } + + /// + /// Returns true if the given entity is considered fragile for delivery. + /// + public bool IsEntityFragile(EntityUid uid, int fragileDamageThreshold) + { + // It takes damage on falling. + if (HasComp(uid)) + return true; + + // It can be spilled easily and has something to spill. + if (HasComp(uid) + && TryComp(uid, out var openable) + && !_openable.IsClosed(uid, null, openable) + && _solutionContainerSystem.PercentFull(uid) > 0) + return true; + + // It might be made of non-reinforced glass. + if (TryComp(uid, out DamageableComponent? damageableComponent) + && damageableComponent.DamageModifierSetId == "Glass") + return true; + + if (!TryComp(uid, out DestructibleComponent? destructibleComp)) + return false; + + // Fallback: It breaks or is destroyed in less than a damage + // threshold dictated by the teleporter. + foreach (var threshold in destructibleComp.Thresholds) + { + if (threshold.Trigger is not DamageTrigger trigger || trigger.Damage >= fragileDamageThreshold) + continue; + + foreach (var behavior in threshold.Behaviors) + { + if (behavior is not DoActsBehavior doActs) + continue; + + if (doActs.Acts.HasFlag(ThresholdActs.Breakage) || doActs.Acts.HasFlag(ThresholdActs.Destruction)) + return true; + } + } + + return false; + } + + public bool TryMatchJobTitleToDepartment(string jobTitle, [NotNullWhen(true)] out string? jobDepartment) + { + foreach (var department in _prototypeManager.EnumeratePrototypes()) + { + foreach (var role in department.Roles) + { + if (!_prototypeManager.TryIndex(role, out JobPrototype? jobPrototype) || jobPrototype.LocalizedName != jobTitle) + continue; + + jobDepartment = department.ID; + return true; + } + } + + jobDepartment = null; + return false; + } + + public bool TryMatchJobTitleToPrototype(string jobTitle, [NotNullWhen(true)] out JobPrototype? jobPrototype) + { + foreach (var job in _prototypeManager.EnumeratePrototypes()) + { + if (job.LocalizedName == jobTitle) + { + jobPrototype = job; + return true; + } + } + + jobPrototype = null; + return false; + } + + /// + /// Handle all the gritty details particular to a new mail entity. + /// + /// + /// This is separate mostly so the unit tests can get to it. + /// + public void SetupMail(EntityUid uid, MailTeleporterComponent component, MailRecipient recipient) + { + var mailComp = EnsureComp(uid); + + var container = _containerSystem.EnsureContainer(uid, "contents"); + foreach (var item in EntitySpawnCollection.GetSpawns(mailComp.Contents, _random)) + { + var entity = EntityManager.SpawnEntity(item, Transform(uid).Coordinates); + if (!_containerSystem.Insert(entity, container)) + { + _sawmill.Error($"Can't insert {ToPrettyString(entity)} into new mail delivery {ToPrettyString(uid)}! Deleting it."); + QueueDel(entity); + } + else if (!mailComp.IsFragile && IsEntityFragile(entity, component.FragileDamageThreshold)) + { + mailComp.IsFragile = true; + } + } + + mailComp.IsPriority = recipient.MayReceivePriorityMail && _random.Prob(component.PriorityChance); + mailComp.RecipientJob = recipient.Job; + mailComp.Recipient = recipient.Name; + + var mailEntityStrings = mailComp.IsLarge ? MailConstants.MailLarge : MailConstants.Mail; + if (mailComp.IsLarge) + { + mailComp.Bounty += component.LargeBonus; + mailComp.Penalty += component.LargeMalus; + } + + if (mailComp.IsFragile) + { + mailComp.Bounty += component.FragileBonus; + mailComp.Penalty += component.FragileMalus; + _appearanceSystem.SetData(uid, MailVisuals.IsFragile, true); + } + + if (mailComp.IsPriority) + { + mailComp.Bounty += component.PriorityBonus; + mailComp.Penalty += component.PriorityMalus; + _appearanceSystem.SetData(uid, MailVisuals.IsPriority, true); + mailComp.PriorityCancelToken = new CancellationTokenSource(); + + Timer.Spawn((int) component.PriorityDuration.TotalMilliseconds, () => + { + // DeltaV - Expired mail recorded to logistic stats + ExecuteForEachLogisticsStats(uid, (station, logisticStats) => + { + _logisticsStatsSystem.AddExpiredMailLosses(station, + logisticStats, + mailComp.IsProfitable ? mailComp.Penalty : 0); + }); + + PenalizeStationFailedDelivery(uid, mailComp, "mail-penalty-expired"); + }, mailComp.PriorityCancelToken.Token); + } + + _appearanceSystem.SetData(uid, MailVisuals.JobIcon, recipient.JobIcon); + + _metaDataSystem.SetEntityName(uid, Loc.GetString(mailEntityStrings.NameAddressed, // Frontier: move constant to MailEntityString + ("recipient", recipient.Name))); + + var accessReader = EnsureComp(uid); + accessReader.AccessLists.Add(recipient.AccessTags); + } + + /// + /// Return the parcels waiting for delivery. + /// + /// The mail teleporter to check. + public List GetUndeliveredParcels(EntityUid uid) + { + // An alternative solution would be to keep a list of the unopened + // parcels spawned by the teleporter and see if they're not carried + // by someone, but this is simple, and simple is good. + List undeliveredParcels = new(); + foreach (var entityInTile in TurfHelpers.GetEntitiesInTile(Transform(uid).Coordinates, LookupFlags.Dynamic | LookupFlags.Sundries)) + { + if (HasComp(entityInTile)) + undeliveredParcels.Add(entityInTile); + } + return undeliveredParcels; + } + + /// + /// Return how many parcels are waiting for delivery. + /// + /// The mail teleporter to check. + public uint GetUndeliveredParcelCount(EntityUid uid) + { + return (uint) GetUndeliveredParcels(uid).Count(); + } + + /// + /// Try to match a mail receiver to a mail teleporter. + /// + public bool TryGetMailTeleporterForReceiver(MailReceiverComponent receiver, [NotNullWhen(true)] out MailTeleporterComponent? teleporterComponent) + { + foreach (var mailTeleporter in EntityQuery()) + { + if (_stationSystem.GetOwningStation(receiver.Owner) != _stationSystem.GetOwningStation(mailTeleporter.Owner)) + continue; + + teleporterComponent = mailTeleporter; + return true; + } + + teleporterComponent = null; + return false; + } + + /// + /// Try to construct a recipient struct for a mail parcel based on a receiver. + /// + public bool TryGetMailRecipientForReceiver(MailReceiverComponent receiver, [NotNullWhen(true)] out MailRecipient? recipient) + { + // Because of the way this works, people are not considered + // candidates for mail if there is no valid PDA or ID in their slot + // or active hand. A better future solution might be checking the + // station records, possibly cross-referenced with the medical crew + // scanner to look for living recipients. TODO + + if (_idCardSystem.TryFindIdCard(receiver.Owner, out var idCard) + && TryComp(idCard.Owner, out var access) + && idCard.Comp.FullName != null + && idCard.Comp.JobTitle != null) + { + var accessTags = access.Tags; + + var mayReceivePriorityMail = !(_mindSystem.GetMind(receiver.Owner) == null); + + recipient = new MailRecipient(idCard.Comp.FullName, + idCard.Comp.JobTitle, + idCard.Comp.JobIcon, + accessTags, + mayReceivePriorityMail); + + return true; + } + + recipient = null; + return false; + } + + /// + /// Get the list of valid mail recipients for a mail teleporter. + /// + public List GetMailRecipientCandidates(EntityUid uid) + { + List candidateList = new(); + + foreach (var receiver in EntityQuery()) + { + if (_stationSystem.GetOwningStation(receiver.Owner) != _stationSystem.GetOwningStation(uid)) + continue; + + if (TryGetMailRecipientForReceiver(receiver, out MailRecipient? recipient)) + candidateList.Add(recipient.Value); + } + + return candidateList; + } + + /// + /// Handle the spawning of all the mail for a mail teleporter. + /// + public void SpawnMail(EntityUid uid, MailTeleporterComponent? component = null) + { + if (!Resolve(uid, ref component)) + { + _sawmill.Error($"Tried to SpawnMail on {ToPrettyString(uid)} without a valid MailTeleporterComponent!"); + return; + } + + if (GetUndeliveredParcelCount(uid) >= component.MaximumUndeliveredParcels) + return; + + var candidateList = GetMailRecipientCandidates(uid); + + if (candidateList.Count <= 0) + { + _sawmill.Error("List of mail candidates was empty!"); + return; + } + + if (!_prototypeManager.TryIndex(component.MailPool, out var pool)) + { + _sawmill.Error($"Can't index {ToPrettyString(uid)}'s MailPool {component.MailPool}!"); + return; + } + + for (int i = 0; + i < component.MinimumDeliveriesPerTeleport + candidateList.Count / component.CandidatesPerDelivery; + i++) + { + var candidate = _random.Pick(candidateList); + var possibleParcels = new Dictionary(pool.Everyone); + + if (TryMatchJobTitleToPrototype(candidate.Job, out JobPrototype? jobPrototype) + && pool.Jobs.TryGetValue(jobPrototype.ID, out Dictionary? jobParcels)) + { + possibleParcels = possibleParcels.Union(jobParcels) + .GroupBy(g => g.Key) + .ToDictionary(pair => pair.Key, pair => pair.First().Value); + } + + if (TryMatchJobTitleToDepartment(candidate.Job, out string? department) + && pool.Departments.TryGetValue(department, out Dictionary? departmentParcels)) + { + possibleParcels = possibleParcels.Union(departmentParcels) + .GroupBy(g => g.Key) + .ToDictionary(pair => pair.Key, pair => pair.First().Value); + } + + var accumulated = 0f; + var randomPoint = _random.NextFloat(possibleParcels.Values.Sum()); + string? chosenParcel = null; + foreach (var (key, weight) in possibleParcels) + { + accumulated += weight; + if (accumulated >= randomPoint) + { + chosenParcel = key; + break; + } + } + + if (chosenParcel == null) + { + _sawmill.Error($"MailSystem wasn't able to find a deliverable parcel for {candidate.Name}, {candidate.Job}!"); + return; + } + + var mail = EntityManager.SpawnEntity(chosenParcel, Transform(uid).Coordinates); + SetupMail(mail, component, candidate); + + _tagSystem.AddTag(mail, "Mail"); // Frontier + } + + if (_containerSystem.TryGetContainer(uid, "queued", out var queued)) + _containerSystem.EmptyContainer(queued); + + _audioSystem.PlayPvs(component.TeleportSound, uid); + } + + public void OpenMail(EntityUid uid, MailComponent? component = null, EntityUid? user = null) + { + if (!Resolve(uid, ref component)) + return; + + _audioSystem.PlayPvs(component.OpenSound, uid); + + if (user != null) + _handsSystem.TryDrop((EntityUid) user); + + if (!_containerSystem.TryGetContainer(uid, "contents", out var contents)) + { + // I silenced this error because it fails non deterministically in tests and doesn't seem to effect anything else. + // _sawmill.Error($"Mail {ToPrettyString(uid)} was missing contents container!"); + return; + } + + foreach (var entity in contents.ContainedEntities.ToArray()) + { + _handsSystem.PickupOrDrop(user, entity); + } + + _tagSystem.AddTag(uid, "Trash"); + _tagSystem.AddTag(uid, "Recyclable"); + component.IsEnabled = false; + UpdateMailTrashState(uid, true); + } + + private void UpdateAntiTamperVisuals(EntityUid uid, bool isLocked) + { + _appearanceSystem.SetData(uid, MailVisuals.IsLocked, isLocked); + } + + private void UpdateMailTrashState(EntityUid uid, bool isTrash) + { + _appearanceSystem.SetData(uid, MailVisuals.IsTrash, isTrash); + } + + // DeltaV - Helper function that executes for each StationLogisticsStatsComponent + // For updating MailMetrics stats + private void ExecuteForEachLogisticsStats(EntityUid uid, + Action action) + { + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var station, out var logisticStats)) + { + if (_stationSystem.GetOwningStation(uid) != station) + continue; + action(station, logisticStats); + } + } +} + +public struct MailRecipient( + string name, + string job, + string jobIcon, + HashSet> accessTags, + bool mayReceivePriorityMail) +{ + public string Name = name; + public string Job = job; + public string JobIcon = jobIcon; + public HashSet> AccessTags = accessTags; + public bool MayReceivePriorityMail = mayReceivePriorityMail; +} diff --git a/Content.Server/Nyanotrasen/Mail/Components/MailComponent.cs b/Content.Server/Nyanotrasen/Mail/Components/MailComponent.cs deleted file mode 100644 index 61d94bbad3..0000000000 --- a/Content.Server/Nyanotrasen/Mail/Components/MailComponent.cs +++ /dev/null @@ -1,108 +0,0 @@ -using System.Threading; -using Robust.Shared.Audio; -using Content.Shared.Storage; -using Content.Shared.Mail; - -namespace Content.Server.Mail.Components -{ - [RegisterComponent] - public sealed partial class MailComponent : SharedMailComponent - { - [ViewVariables(VVAccess.ReadWrite)] - [DataField("recipient")] - public string Recipient = "None"; - - [ViewVariables(VVAccess.ReadWrite)] - [DataField("recipientJob")] - public string RecipientJob = "None"; - - // Why do we not use LockComponent? - // Because this can't be locked again, - // and we have special conditions for unlocking, - // and we don't want to add a verb. - [ViewVariables(VVAccess.ReadWrite)] - [DataField("isLocked")] - public bool IsLocked = true; - - /// - /// Is this parcel profitable to deliver for the station? - /// - /// - /// The station won't receive any award on delivery if this is false. - /// This is useful for broken fragile packages and packages that were - /// not delivered in time. - /// - [DataField("isProfitable")] - public bool IsProfitable = true; - - /// - /// Is this package considered fragile? - /// - /// - /// This can be set to true in the YAML files for a mail delivery to - /// always be Fragile, despite its contents. - /// - [DataField("isFragile")] - public bool IsFragile = false; - - /// - /// Is this package considered priority mail? - /// - /// - /// There will be a timer set for its successful delivery. The - /// station's bank account will be penalized if it is not delivered on - /// time. - /// - /// This is set to false on successful delivery. - /// - /// This can be set to true in the YAML files for a mail delivery to - /// always be Priority. - /// - [DataField("isPriority")] - public bool IsPriority = false; - - /// - /// What will be packaged when the mail is spawned. - /// - [DataField("contents")] - public List Contents = new(); - - /// - /// The amount that cargo will be awarded for delivering this mail. - /// - [DataField("bounty")] - public int Bounty = 750; - - /// - /// Penalty if the mail is destroyed. - /// - [DataField("penalty")] - public int Penalty = -250; - - /// - /// The sound that's played when the mail's lock is broken. - /// - [DataField("penaltySound")] - public SoundSpecifier PenaltySound = new SoundPathSpecifier("/Audio/Machines/Nuke/angry_beep.ogg"); - - /// - /// The sound that's played when the mail's opened. - /// - [DataField("openSound")] - public SoundSpecifier OpenSound = new SoundPathSpecifier("/Audio/Effects/packetrip.ogg"); - - /// - /// The sound that's played when the mail's lock has been emagged. - /// - [DataField("emagSound")] - public SoundSpecifier EmagSound = new SoundCollectionSpecifier("sparks"); - - /// - /// Whether this component is enabled. - /// Removed when it becomes trash. - /// - public bool IsEnabled = true; - - public CancellationTokenSource? priorityCancelToken; - } -} diff --git a/Content.Server/Nyanotrasen/Mail/Components/MailReceiverComponent.cs b/Content.Server/Nyanotrasen/Mail/Components/MailReceiverComponent.cs deleted file mode 100644 index 4224de5de4..0000000000 --- a/Content.Server/Nyanotrasen/Mail/Components/MailReceiverComponent.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Content.Server.Mail.Components -{ - [RegisterComponent] - public sealed partial class MailReceiverComponent : Component - {} -} diff --git a/Content.Server/Nyanotrasen/Mail/Components/MailTeleporterComponent.cs b/Content.Server/Nyanotrasen/Mail/Components/MailTeleporterComponent.cs deleted file mode 100644 index bc05d7307d..0000000000 --- a/Content.Server/Nyanotrasen/Mail/Components/MailTeleporterComponent.cs +++ /dev/null @@ -1,108 +0,0 @@ -using Robust.Shared.Audio; - -namespace Content.Server.Mail.Components -{ - /// - /// This is for the mail teleporter. - /// Random mail will be teleported to this every few minutes. - /// - [RegisterComponent] - public sealed partial class MailTeleporterComponent : Component - { - - // Not starting accumulator at 0 so mail carriers have some deliveries to make shortly after roundstart. - [DataField("accumulator")] - public float Accumulator = 285f; - - [DataField("teleportInterval")] - public TimeSpan TeleportInterval = TimeSpan.FromMinutes(5); - - /// - /// The sound that's played when new mail arrives. - /// - [DataField("teleportSound")] - public SoundSpecifier TeleportSound = new SoundPathSpecifier("/Audio/Effects/teleport_arrival.ogg"); - - /// - /// The MailDeliveryPoolPrototype that's used to select what mail this - /// teleporter can deliver. - /// - [DataField("mailPool")] - public string MailPool = "RandomMailDeliveryPool"; - - /// - /// How many mail candidates do we need per actual delivery sent when - /// the mail goes out? The number of candidates is divided by this number - /// to determine how many deliveries will be teleported in. - /// It does not determine unique recipients. That is random. - /// - [DataField("candidatesPerDelivery")] - public int CandidatesPerDelivery = 8; - - [DataField("minimumDeliveriesPerTeleport")] - public int MinimumDeliveriesPerTeleport = 1; - - /// - /// Do not teleport any more mail in, if there are at least this many - /// undelivered parcels. - /// - /// - /// Currently this works by checking how many MailComponent entities - /// are sitting on the teleporter's tile. - /// - /// It should be noted that if the number of actual deliveries to be - /// made based on the number of candidates divided by candidates per - /// delivery exceeds this number, the teleporter will spawn more mail - /// than this number. - /// - /// This is just a simple check to see if anyone's been picking up the - /// mail lately to prevent entity bloat for the sake of performance. - /// - [DataField("maximumUndeliveredParcels")] - public int MaximumUndeliveredParcels = 5; - - /// - /// Any item that breaks or is destroyed in less than this amount of - /// damage is one of the types of items considered fragile. - /// - [DataField("fragileDamageThreshold")] - public int FragileDamageThreshold = 10; - - /// - /// What's the bonus for delivering a fragile package intact? - /// - [DataField("fragileBonus")] - public int FragileBonus = 100; - - /// - /// What's the malus for failing to deliver a fragile package? - /// - [DataField("fragileMalus")] - public int FragileMalus = -100; - - /// - /// What's the chance for any one delivery to be marked as priority mail? - /// - [DataField("priorityChance")] - public float PriorityChance = 0.1f; - - /// - /// How long until a priority delivery is considered as having failed - /// if not delivered? - /// - [DataField("priorityDuration")] - public TimeSpan priorityDuration = TimeSpan.FromMinutes(5); - - /// - /// What's the bonus for delivering a priority package on time? - /// - [DataField("priorityBonus")] - public int PriorityBonus = 250; - - /// - /// What's the malus for failing to deliver a priority package? - /// - [DataField("priorityMalus")] - public int PriorityMalus = -250; - } -} diff --git a/Content.Server/Nyanotrasen/Mail/MailSystem.cs b/Content.Server/Nyanotrasen/Mail/MailSystem.cs deleted file mode 100644 index 05cd0c88a7..0000000000 --- a/Content.Server/Nyanotrasen/Mail/MailSystem.cs +++ /dev/null @@ -1,731 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Threading; -using Robust.Shared.Audio; -using Robust.Shared.Containers; -using Robust.Shared.Prototypes; -using Robust.Shared.Random; -using Content.Server.Access.Systems; -using Content.Server.Cargo.Components; -using Content.Server.Cargo.Systems; -using Content.Server.Chat.Systems; -using Content.Server.Chemistry.Containers.EntitySystems; -using Content.Server.Chemistry.EntitySystems; -using Content.Server.Damage.Components; -using Content.Server.Destructible; -using Content.Server.Destructible.Thresholds; -using Content.Server.Destructible.Thresholds.Behaviors; -using Content.Server.Destructible.Thresholds.Triggers; -using Content.Server.Fluids.Components; -using Content.Server.Item; -using Content.Server.Mail.Components; -using Content.Server.Mind; -using Content.Server.Nutrition.Components; -using Content.Server.Nutrition.EntitySystems; -using Content.Server.Popups; -using Content.Server.Power.Components; -using Content.Server.Station.Systems; -using Content.Server.Spawners.EntitySystems; -using Content.Shared.Access; -using Content.Shared.Access.Components; -using Content.Shared.Access.Systems; -using Content.Shared.Chat; -using Content.Shared.Chemistry.EntitySystems; -using Content.Shared.Damage; -using Content.Shared.Emag.Components; -using Content.Shared.Destructible; -using Content.Shared.Emag.Systems; -using Content.Shared.Examine; -using Content.Shared.Fluids.Components; -using Content.Shared.Hands.EntitySystems; -using Content.Shared.Interaction; -using Content.Shared.Interaction.Events; -using Content.Shared.Item; -using Content.Shared.Mail; -using Content.Shared.Maps; -using Content.Shared.Nutrition.Components; -using Content.Shared.Nutrition.EntitySystems; -using Content.Shared.PDA; -using Content.Shared.Random.Helpers; -using Content.Shared.Roles; -using Content.Shared.StatusIcon; -using Content.Shared.Storage; -using Content.Shared.Tag; -using Robust.Shared.Audio.Systems; -using Timer = Robust.Shared.Timing.Timer; - -namespace Content.Server.Mail -{ - public sealed class MailSystem : EntitySystem - { - [Dependency] private readonly PopupSystem _popupSystem = default!; - [Dependency] private readonly AccessReaderSystem _accessSystem = default!; - [Dependency] private readonly SharedHandsSystem _handsSystem = default!; - [Dependency] private readonly IdCardSystem _idCardSystem = default!; - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly TagSystem _tagSystem = default!; - [Dependency] private readonly CargoSystem _cargoSystem = default!; - [Dependency] private readonly StationSystem _stationSystem = default!; - [Dependency] private readonly ChatSystem _chatSystem = default!; - [Dependency] private readonly OpenableSystem _openable = default!; - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly SharedContainerSystem _containerSystem = default!; - [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; - [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; - [Dependency] private readonly SharedAudioSystem _audioSystem = default!; - [Dependency] private readonly DamageableSystem _damageableSystem = default!; - [Dependency] private readonly ItemSystem _itemSystem = default!; - [Dependency] private readonly MindSystem _mindSystem = default!; - [Dependency] private readonly MetaDataSystem _metaDataSystem = default!; - - private ISawmill _sawmill = default!; - - public override void Initialize() - { - base.Initialize(); - - _sawmill = Logger.GetSawmill("mail"); - - SubscribeLocalEvent(OnSpawnPlayer, after: new[] { typeof(SpawnPointSystem) }); - - SubscribeLocalEvent(OnRemove); - SubscribeLocalEvent(OnUseInHand); - SubscribeLocalEvent(OnAfterInteractUsing); - SubscribeLocalEvent(OnExamined); - SubscribeLocalEvent(OnDestruction); - SubscribeLocalEvent(OnDamage); - SubscribeLocalEvent(OnBreak); - SubscribeLocalEvent(OnMailEmagged); - } - - public override void Update(float frameTime) - { - base.Update(frameTime); - foreach (var mailTeleporter in EntityQuery()) - { - if (TryComp(mailTeleporter.Owner, out var power) && !power.Powered) - return; - - mailTeleporter.Accumulator += frameTime; - - if (mailTeleporter.Accumulator < mailTeleporter.TeleportInterval.TotalSeconds) - continue; - - mailTeleporter.Accumulator -= (float) mailTeleporter.TeleportInterval.TotalSeconds; - - SpawnMail(mailTeleporter.Owner, mailTeleporter); - } - } - - /// - /// Dynamically add the MailReceiver component to appropriate entities. - /// - private void OnSpawnPlayer(PlayerSpawningEvent args) - { - if (args.SpawnResult == null || - args.Job == null || - args.Station is not {} station) - { - return; - } - - if (!HasComp(station)) - return; - - AddComp(args.SpawnResult.Value); - } - - private void OnRemove(EntityUid uid, MailComponent component, ComponentRemove args) - { - // Make sure the priority timer doesn't run. - if (component.priorityCancelToken != null) - component.priorityCancelToken.Cancel(); - } - - /// - /// Try to open the mail. - /// - private void OnUseInHand(EntityUid uid, MailComponent component, UseInHandEvent args) - { - if (!component.IsEnabled) - return; - if (component.IsLocked) - { - _popupSystem.PopupEntity(Loc.GetString("mail-locked"), uid, args.User); - return; - } - OpenMail(uid, component, args.User); - } - - /// - /// Handle logic similar between a normal mail unlock and an emag - /// frying out the lock. - /// - private void UnlockMail(EntityUid uid, MailComponent component) - { - component.IsLocked = false; - UpdateAntiTamperVisuals(uid, false); - - if (component.IsPriority) - { - // This is a successful delivery. Keep the failure timer from triggering. - if (component.priorityCancelToken != null) - component.priorityCancelToken.Cancel(); - - // The priority tape is visually considered to be a part of the - // anti-tamper lock, so remove that too. - _appearanceSystem.SetData(uid, MailVisuals.IsPriority, false); - - // The examination code depends on this being false to not show - // the priority tape description anymore. - component.IsPriority = false; - } - } - - /// - /// Check the ID against the mail's lock - /// - private void OnAfterInteractUsing(EntityUid uid, MailComponent component, AfterInteractUsingEvent args) - { - if (!args.CanReach || !component.IsLocked) - return; - - if (!TryComp(uid, out var access)) - return; - - IdCardComponent? idCard = null; // We need an ID card. - - if (HasComp(args.Used)) /// Can we find it in a PDA if the user is using that? - { - _idCardSystem.TryGetIdCard(args.Used, out var pdaID); - idCard = pdaID; - } - - if (HasComp(args.Used)) /// Or are they using an id card directly? - idCard = Comp(args.Used); - - if (idCard == null) /// Return if we still haven't found an id card. - return; - - if (!HasComp(uid)) - { - if (idCard.FullName != component.Recipient || idCard.JobTitle != component.RecipientJob) - { - _popupSystem.PopupEntity(Loc.GetString("mail-recipient-mismatch"), uid, args.User); - return; - } - - if (!_accessSystem.IsAllowed(uid, args.User)) - { - _popupSystem.PopupEntity(Loc.GetString("mail-invalid-access"), uid, args.User); - return; - } - } - - UnlockMail(uid, component); - - if (!component.IsProfitable) - { - _popupSystem.PopupEntity(Loc.GetString("mail-unlocked"), uid, args.User); - return; - } - - _popupSystem.PopupEntity(Loc.GetString("mail-unlocked-reward", ("bounty", component.Bounty)), uid, args.User); - - component.IsProfitable = false; - - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var station, out var account)) - { - if (_stationSystem.GetOwningStation(uid) != station) - continue; - - _cargoSystem.UpdateBankAccount(station, account, component.Bounty); - return; - } - } - - private void OnExamined(EntityUid uid, MailComponent component, ExaminedEvent args) - { - if (!args.IsInDetailsRange) - { - args.PushMarkup(Loc.GetString("mail-desc-far")); - return; - } - - args.PushMarkup(Loc.GetString("mail-desc-close", ("name", component.Recipient), ("job", component.RecipientJob))); - - if (component.IsFragile) - args.PushMarkup(Loc.GetString("mail-desc-fragile")); - - if (component.IsPriority) - { - if (component.IsProfitable) - args.PushMarkup(Loc.GetString("mail-desc-priority")); - else - args.PushMarkup(Loc.GetString("mail-desc-priority-inactive")); - } - } - - /// - /// Penalize a station for a failed delivery. - /// - /// - /// This will mark a parcel as no longer being profitable, which will - /// prevent multiple failures on different conditions for the same - /// delivery. - /// - /// The standard penalization is breaking the anti-tamper lock, - /// but this allows a delivery to fail for other reasons too - /// while having a generic function to handle different messages. - /// - public void PenalizeStationFailedDelivery(EntityUid uid, MailComponent component, string localizationString) - { - if (!component.IsProfitable) - return; - - _chatSystem.TrySendInGameICMessage(uid, Loc.GetString(localizationString, ("credits", component.Penalty)), InGameICChatType.Speak, false); - _audioSystem.PlayPvs(component.PenaltySound, uid); - - component.IsProfitable = false; - - if (component.IsPriority) - _appearanceSystem.SetData(uid, MailVisuals.IsPriorityInactive, true); - - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var station, out var account)) - { - if (_stationSystem.GetOwningStation(uid) != station) - continue; - - _cargoSystem.UpdateBankAccount(station, account, component.Penalty); - return; - } - } - - private void OnDestruction(EntityUid uid, MailComponent component, DestructionEventArgs args) - { - if (component.IsLocked) - PenalizeStationFailedDelivery(uid, component, "mail-penalty-lock"); - - if (component.IsEnabled) - OpenMail(uid, component); - - UpdateAntiTamperVisuals(uid, false); - } - - private void OnDamage(EntityUid uid, MailComponent component, DamageChangedEvent args) - { - if (args.DamageDelta == null) - return; - - if (!_containerSystem.TryGetContainer(uid, "contents", out var contents)) - return; - - // Transfer damage to the contents. - // This should be a general-purpose feature for all containers in the future. - foreach (var entity in contents.ContainedEntities.ToArray()) - { - _damageableSystem.TryChangeDamage(entity, args.DamageDelta); - } - } - - private void OnBreak(EntityUid uid, MailComponent component, BreakageEventArgs args) - { - _appearanceSystem.SetData(uid, MailVisuals.IsBroken, true); - - if (component.IsFragile) - PenalizeStationFailedDelivery(uid, component, "mail-penalty-fragile"); - } - - private void OnMailEmagged(EntityUid uid, MailComponent component, ref GotEmaggedEvent args) - { - if (!component.IsLocked) - return; - - UnlockMail(uid, component); - - _popupSystem.PopupEntity(Loc.GetString("mail-unlocked-by-emag"), uid, args.UserUid); - - _audioSystem.PlayPvs(component.EmagSound, uid, AudioParams.Default.WithVolume(4)); - component.IsProfitable = false; - args.Handled = true; - } - - /// - /// Returns true if the given entity is considered fragile for delivery. - /// - public bool IsEntityFragile(EntityUid uid, int fragileDamageThreshold) - { - // It takes damage on falling. - if (HasComp(uid)) - return true; - - // It can be spilled easily and has something to spill. - if (HasComp(uid) - && TryComp(uid, out var openable) - && !_openable.IsClosed(uid, null, openable) - && _solutionContainerSystem.PercentFull(uid) > 0) - return true; - - // It might be made of non-reinforced glass. - if (TryComp(uid, out DamageableComponent? damageableComponent) - && damageableComponent.DamageModifierSetId == "Glass") - return true; - - // Fallback: It breaks or is destroyed in less than a damage - // threshold dictated by the teleporter. - if (TryComp(uid, out DestructibleComponent? destructibleComp)) - { - foreach (var threshold in destructibleComp.Thresholds) - { - if (threshold.Trigger is DamageTrigger trigger - && trigger.Damage < fragileDamageThreshold) - { - foreach (var behavior in threshold.Behaviors) - { - if (behavior is DoActsBehavior doActs) - { - if (doActs.Acts.HasFlag(ThresholdActs.Breakage) - || doActs.Acts.HasFlag(ThresholdActs.Destruction)) - { - return true; - } - } - } - } - } - } - - return false; - } - - public bool TryMatchJobTitleToDepartment(string jobTitle, [NotNullWhen(true)] out string? jobDepartment) - { - foreach (var department in _prototypeManager.EnumeratePrototypes()) - { - foreach (var role in department.Roles) - { - if (_prototypeManager.TryIndex(role, out JobPrototype? _jobPrototype) - && _jobPrototype.LocalizedName == jobTitle) - { - jobDepartment = department.ID; - return true; - } - } - } - - jobDepartment = null; - return false; - } - - public bool TryMatchJobTitleToPrototype(string jobTitle, [NotNullWhen(true)] out JobPrototype? jobPrototype) - { - foreach (var job in _prototypeManager.EnumeratePrototypes()) - { - if (job.LocalizedName == jobTitle) - { - jobPrototype = job; - return true; - } - } - - jobPrototype = null; - return false; - } - - /// - /// Handle all the gritty details particular to a new mail entity. - /// - /// - /// This is separate mostly so the unit tests can get to it. - /// - public void SetupMail(EntityUid uid, MailTeleporterComponent component, MailRecipient recipient) - { - var mailComp = EnsureComp(uid); - - var container = _containerSystem.EnsureContainer(uid, "contents"); - foreach (var item in EntitySpawnCollection.GetSpawns(mailComp.Contents, _random)) - { - var entity = EntityManager.SpawnEntity(item, Transform(uid).Coordinates); - if (!_containerSystem.Insert(entity, container)) - { - _sawmill.Error($"Can't insert {ToPrettyString(entity)} into new mail delivery {ToPrettyString(uid)}! Deleting it."); - QueueDel(entity); - } - else if (!mailComp.IsFragile && IsEntityFragile(entity, component.FragileDamageThreshold)) - { - mailComp.IsFragile = true; - } - } - - if (_random.Prob(component.PriorityChance)) - mailComp.IsPriority = true; - - // This needs to override both the random probability and the - // entity prototype, so this is fine. - if (!recipient.MayReceivePriorityMail) - mailComp.IsPriority = false; - - mailComp.RecipientJob = recipient.Job; - mailComp.Recipient = recipient.Name; - - if (mailComp.IsFragile) - { - mailComp.Bounty += component.FragileBonus; - mailComp.Penalty += component.FragileMalus; - _appearanceSystem.SetData(uid, MailVisuals.IsFragile, true); - } - - if (mailComp.IsPriority) - { - mailComp.Bounty += component.PriorityBonus; - mailComp.Penalty += component.PriorityMalus; - _appearanceSystem.SetData(uid, MailVisuals.IsPriority, true); - - mailComp.priorityCancelToken = new CancellationTokenSource(); - - Timer.Spawn((int) component.priorityDuration.TotalMilliseconds, - () => PenalizeStationFailedDelivery(uid, mailComp, "mail-penalty-expired"), - mailComp.priorityCancelToken.Token); - } - - _appearanceSystem.SetData(uid, MailVisuals.JobIcon, recipient.JobIcon); - - _metaDataSystem.SetEntityName(uid, Loc.GetString("mail-item-name-addressed", - ("recipient", recipient.Name))); - - var accessReader = EnsureComp(uid); - accessReader.AccessLists.Add(recipient.AccessTags); - } - - /// - /// Return the parcels waiting for delivery. - /// - /// The mail teleporter to check. - public List GetUndeliveredParcels(EntityUid uid) - { - // An alternative solution would be to keep a list of the unopened - // parcels spawned by the teleporter and see if they're not carried - // by someone, but this is simple, and simple is good. - List undeliveredParcels = new(); - foreach (var entityInTile in TurfHelpers.GetEntitiesInTile(Transform(uid).Coordinates, LookupFlags.Dynamic | LookupFlags.Sundries)) - { - if (HasComp(entityInTile)) - undeliveredParcels.Add(entityInTile); - } - return undeliveredParcels; - } - - /// - /// Return how many parcels are waiting for delivery. - /// - /// The mail teleporter to check. - public uint GetUndeliveredParcelCount(EntityUid uid) - { - return (uint) GetUndeliveredParcels(uid).Count(); - } - - /// - /// Try to match a mail receiver to a mail teleporter. - /// - public bool TryGetMailTeleporterForReceiver(MailReceiverComponent receiver, [NotNullWhen(true)] out MailTeleporterComponent? teleporterComponent) - { - foreach (var mailTeleporter in EntityQuery()) - { - if (_stationSystem.GetOwningStation(receiver.Owner) == _stationSystem.GetOwningStation(mailTeleporter.Owner)) - { - teleporterComponent = mailTeleporter; - return true; - } - } - - teleporterComponent = null; - return false; - } - - /// - /// Try to construct a recipient struct for a mail parcel based on a receiver. - /// - public bool TryGetMailRecipientForReceiver(MailReceiverComponent receiver, [NotNullWhen(true)] out MailRecipient? recipient) - { - // Because of the way this works, people are not considered - // candidates for mail if there is no valid PDA or ID in their slot - // or active hand. A better future solution might be checking the - // station records, possibly cross-referenced with the medical crew - // scanner to look for living recipients. TODO - - if (_idCardSystem.TryFindIdCard(receiver.Owner, out var idCard) - && TryComp(idCard.Owner, out var access) - && idCard.Comp.FullName != null - && idCard.Comp.JobTitle != null) - { - var accessTags = access.Tags; - - var mayReceivePriorityMail = !(_mindSystem.GetMind(receiver.Owner) == null); - - recipient = new MailRecipient(idCard.Comp.FullName, - idCard.Comp.JobTitle, - idCard.Comp.JobIcon, - accessTags, - mayReceivePriorityMail); - - return true; - } - - recipient = null; - return false; - } - - /// - /// Get the list of valid mail recipients for a mail teleporter. - /// - public List GetMailRecipientCandidates(EntityUid uid) - { - List candidateList = new(); - - foreach (var receiver in EntityQuery()) - { - if (_stationSystem.GetOwningStation(receiver.Owner) != _stationSystem.GetOwningStation(uid)) - continue; - - if (TryGetMailRecipientForReceiver(receiver, out MailRecipient? recipient)) - candidateList.Add(recipient.Value); - } - - return candidateList; - } - - /// - /// Handle the spawning of all the mail for a mail teleporter. - /// - public void SpawnMail(EntityUid uid, MailTeleporterComponent? component = null) - { - if (!Resolve(uid, ref component)) - { - _sawmill.Error($"Tried to SpawnMail on {ToPrettyString(uid)} without a valid MailTeleporterComponent!"); - return; - } - - if (GetUndeliveredParcelCount(uid) >= component.MaximumUndeliveredParcels) - return; - - var candidateList = GetMailRecipientCandidates(uid); - - if (candidateList.Count <= 0) - { - _sawmill.Error("List of mail candidates was empty!"); - return; - } - - if (!_prototypeManager.TryIndex(component.MailPool, out var pool)) - { - _sawmill.Error($"Can't index {ToPrettyString(uid)}'s MailPool {component.MailPool}!"); - return; - } - - for (int i = 0; - i < component.MinimumDeliveriesPerTeleport + candidateList.Count / component.CandidatesPerDelivery; - i++) - { - var candidate = _random.Pick(candidateList); - var possibleParcels = new Dictionary(pool.Everyone); - - if (TryMatchJobTitleToPrototype(candidate.Job, out JobPrototype? jobPrototype) - && pool.Jobs.TryGetValue(jobPrototype.ID, out Dictionary? jobParcels)) - { - possibleParcels = possibleParcels.Union(jobParcels) - .GroupBy(g => g.Key) - .ToDictionary(pair => pair.Key, pair => pair.First().Value); - } - - if (TryMatchJobTitleToDepartment(candidate.Job, out string? department) - && pool.Departments.TryGetValue(department, out Dictionary? departmentParcels)) - { - possibleParcels = possibleParcels.Union(departmentParcels) - .GroupBy(g => g.Key) - .ToDictionary(pair => pair.Key, pair => pair.First().Value); - } - - var accumulated = 0f; - var randomPoint = _random.NextFloat(possibleParcels.Values.Sum()); - string? chosenParcel = null; - foreach (var (key, weight) in possibleParcels) - { - accumulated += weight; - if (accumulated >= randomPoint) - { - chosenParcel = key; - break; - } - } - - if (chosenParcel == null) - { - _sawmill.Error($"MailSystem wasn't able to find a deliverable parcel for {candidate.Name}, {candidate.Job}!"); - return; - } - - var mail = EntityManager.SpawnEntity(chosenParcel, Transform(uid).Coordinates); - SetupMail(mail, component, candidate); - } - - if (_containerSystem.TryGetContainer(uid, "queued", out var queued)) - _containerSystem.EmptyContainer(queued); - - _audioSystem.PlayPvs(component.TeleportSound, uid); - } - - public void OpenMail(EntityUid uid, MailComponent? component = null, EntityUid? user = null) - { - if (!Resolve(uid, ref component)) - return; - - _audioSystem.PlayPvs(component.OpenSound, uid); - - if (user != null) - _handsSystem.TryDrop((EntityUid) user); - - if (!_containerSystem.TryGetContainer(uid, "contents", out var contents)) - { - // I silenced this error because it fails non deterministically in tests and doesn't seem to effect anything else. - // _sawmill.Error($"Mail {ToPrettyString(uid)} was missing contents container!"); - return; - } - - foreach (var entity in contents.ContainedEntities.ToArray()) - { - _handsSystem.PickupOrDrop(user, entity); - } - - _tagSystem.AddTag(uid, "Trash"); - _tagSystem.AddTag(uid, "Recyclable"); - component.IsEnabled = false; - UpdateMailTrashState(uid, true); - } - - private void UpdateAntiTamperVisuals(EntityUid uid, bool isLocked) - { - _appearanceSystem.SetData(uid, MailVisuals.IsLocked, isLocked); - } - - private void UpdateMailTrashState(EntityUid uid, bool isTrash) - { - _appearanceSystem.SetData(uid, MailVisuals.IsTrash, isTrash); - } - } - - public struct MailRecipient( - string name, - string job, - string jobIcon, - HashSet> accessTags, - bool mayReceivePriorityMail) - { - public string Name = name; - public string Job = job; - public string JobIcon = jobIcon; - public HashSet> AccessTags = accessTags; - public bool MayReceivePriorityMail = mayReceivePriorityMail; - } -} diff --git a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs index ae742cf1f9..c855bacfc7 100644 --- a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs +++ b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs @@ -425,7 +425,7 @@ public override void Update(float frameTime) if (xform.MapUid != arrivalsXform.MapUid) { if (arrivals.IsValid()) - _shuttles.FTLToDock(uid, shuttle, arrivals); + _shuttles.FTLToDock(uid, shuttle, arrivals, _cfgManager.GetCVar(CCVars.ArrivalsStartupTime), _cfgManager.GetCVar(CCVars.ArrivalsHyperspaceTime), "DockArrivals"); comp.NextArrivalsTime = _timing.CurTime + TimeSpan.FromSeconds(tripTime); } @@ -435,7 +435,7 @@ public override void Update(float frameTime) var targetGrid = _station.GetLargestGrid(data); if (targetGrid != null) - _shuttles.FTLToDock(uid, shuttle, targetGrid.Value); + _shuttles.FTLToDock(uid, shuttle, targetGrid.Value, _cfgManager.GetCVar(CCVars.ArrivalsStartupTime), _cfgManager.GetCVar(CCVars.ArrivalsHyperspaceTime), "DockArrivals"); // The ArrivalsCooldown includes the trip there, so we only need to add the time taken for // the trip back. diff --git a/Content.Server/Standing/LayingDownSystem.cs b/Content.Server/Standing/LayingDownSystem.cs index e5054bdd70..fcad649c9a 100644 --- a/Content.Server/Standing/LayingDownSystem.cs +++ b/Content.Server/Standing/LayingDownSystem.cs @@ -1,6 +1,11 @@ using Content.Shared.Standing; using Content.Shared.CCVar; +using Content.Shared.Input; +using Content.Shared.Movement.Systems; +using Content.Shared.Popups; using Robust.Shared.Configuration; +using Robust.Shared.Input.Binding; +using Robust.Shared.Player; namespace Content.Server.Standing; diff --git a/Content.Server/Traits/Assorted/LayingDownModifierSystem.cs b/Content.Server/Traits/Assorted/LayingDownModifierSystem.cs index e4a63d6108..8c47d65233 100644 --- a/Content.Server/Traits/Assorted/LayingDownModifierSystem.cs +++ b/Content.Server/Traits/Assorted/LayingDownModifierSystem.cs @@ -17,6 +17,6 @@ private void OnStartup(EntityUid uid, LayingDownModifierComponent component, Com return; layingDown.StandingUpTime *= component.LayingDownCooldownMultiplier; - layingDown.SpeedModify *= component.DownedSpeedMultiplierMultiplier; + layingDown.LyingSpeedModifier *= component.DownedSpeedMultiplierMultiplier; } } diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index ddf657bfb3..08401416d7 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -1513,6 +1513,18 @@ public static readonly CVarDef public static readonly CVarDef ArrivalsCooldown = CVarDef.Create("shuttle.arrivals_cooldown", 50f, CVar.SERVERONLY); + /// + /// Time it takes the shuttle to spin up it's hyper drive and jump + /// + public static readonly CVarDef ArrivalsStartupTime= + CVarDef.Create("shuttle.arrivals_startup_time", 5.5f, CVar.SERVERONLY); + + /// + /// Time spent in hyperspace + /// + public static readonly CVarDef ArrivalsHyperspaceTime = + CVarDef.Create("shuttle.arrivals_hyperspace_time", 20f, CVar.SERVERONLY); + /// /// Are players allowed to return on the arrivals shuttle. /// @@ -2475,11 +2487,10 @@ public static readonly CVarDef CVarDef.Create("rest.hold_look_up", false, CVar.CLIENT | CVar.ARCHIVE); /// - /// When true, entities that fall to the ground will be able to crawl under tables and - /// plastic flaps, allowing them to take cover from gunshots. + /// When true, players can choose to crawl under tables while laying down, using the designated keybind. /// public static readonly CVarDef CrawlUnderTables = - CVarDef.Create("rest.crawlundertables", false, CVar.REPLICATED); + CVarDef.Create("rest.crawlundertables", true, CVar.SERVER | CVar.ARCHIVE); #endregion diff --git a/Content.Shared/DeltaV/CartridgeLoader/Cartridges/MailMetricUiState.cs b/Content.Shared/DeltaV/CartridgeLoader/Cartridges/MailMetricUiState.cs new file mode 100644 index 0000000000..69506f5d0c --- /dev/null +++ b/Content.Shared/DeltaV/CartridgeLoader/Cartridges/MailMetricUiState.cs @@ -0,0 +1,50 @@ +using Content.Shared.Security; +using Robust.Shared.Serialization; + +namespace Content.Shared.CartridgeLoader.Cartridges; + +[Serializable, NetSerializable] +public sealed class MailMetricUiState : BoundUserInterfaceState +{ + public readonly MailStats Metrics; + public int UnopenedMailCount { get; } + public int TotalMail { get; } + public double SuccessRate { get; } + + public MailMetricUiState(MailStats metrics, int unopenedMailCount) + { + Metrics = metrics; + UnopenedMailCount = unopenedMailCount; + TotalMail = metrics.TotalMail(unopenedMailCount); + SuccessRate = metrics.SuccessRate(unopenedMailCount); + } +} + +[DataDefinition] +[Serializable, NetSerializable] +public partial record struct MailStats +{ + public int Earnings { get; init; } + public int DamagedLosses { get; init; } + public int ExpiredLosses { get; init; } + public int TamperedLosses { get; init; } + public int OpenedCount { get; init; } + public int DamagedCount { get; init; } + public int ExpiredCount { get; init; } + public int TamperedCount { get; init; } + + public readonly int TotalMail(int unopenedCount) + { + return OpenedCount + unopenedCount; + } + + public readonly int TotalIncome => Earnings + DamagedLosses + ExpiredLosses + TamperedLosses; + + public readonly double SuccessRate(int unopenedCount) + { + var totalMail = TotalMail(unopenedCount); + return (totalMail > 0) + ? Math.Round((double)OpenedCount / totalMail * 100, 2) + : 0; + } +} \ No newline at end of file diff --git a/Content.Shared/Input/ContentKeyFunctions.cs b/Content.Shared/Input/ContentKeyFunctions.cs index f85983282c..1f4e1b9a67 100644 --- a/Content.Shared/Input/ContentKeyFunctions.cs +++ b/Content.Shared/Input/ContentKeyFunctions.cs @@ -57,6 +57,7 @@ public static class ContentKeyFunctions public static readonly BoundKeyFunction ResetZoom = "ResetZoom"; public static readonly BoundKeyFunction OfferItem = "OfferItem"; public static readonly BoundKeyFunction ToggleStanding = "ToggleStanding"; + public static readonly BoundKeyFunction ToggleCrawlingUnder = "ToggleCrawlingUnder"; public static readonly BoundKeyFunction LookUp = "LookUp"; public static readonly BoundKeyFunction ArcadeUp = "ArcadeUp"; diff --git a/Content.Shared/Preferences/HumanoidCharacterProfile.cs b/Content.Shared/Preferences/HumanoidCharacterProfile.cs index 0b316c5128..220c0ecddb 100644 --- a/Content.Shared/Preferences/HumanoidCharacterProfile.cs +++ b/Content.Shared/Preferences/HumanoidCharacterProfile.cs @@ -534,9 +534,13 @@ public void EnsureValid(ICommonSession session, IDependencyCollection collection name = GetName(Species, gender); } - var customspeciename = speciesPrototype.CustomName - ? FormattedMessage.RemoveMarkup(Customspeciename ?? "")[..MaxNameLength] - : ""; + var customspeciename = + !speciesPrototype.CustomName + || string.IsNullOrEmpty(Customspeciename) + ? "" + : Customspeciename.Length > MaxNameLength + ? FormattedMessage.RemoveMarkup(Customspeciename)[..MaxNameLength] + : FormattedMessage.RemoveMarkup(Customspeciename); string flavortext; if (FlavorText.Length > MaxDescLength) diff --git a/Content.Shared/Standing/LayingDownComponent.cs b/Content.Shared/Standing/LayingDownComponent.cs index b3c07220e1..ec9351e22c 100644 --- a/Content.Shared/Standing/LayingDownComponent.cs +++ b/Content.Shared/Standing/LayingDownComponent.cs @@ -1,6 +1,5 @@ using Robust.Shared.GameStates; using Robust.Shared.Serialization; -using Content.Shared.DrawDepth; namespace Content.Shared.Standing; @@ -8,19 +7,24 @@ namespace Content.Shared.Standing; public sealed partial class LayingDownComponent : Component { [DataField, AutoNetworkedField] - public float StandingUpTime { get; set; } = 1f; + public TimeSpan StandingUpTime = TimeSpan.FromSeconds(1); [DataField, AutoNetworkedField] - public float SpeedModify { get; set; } = 0.4f; + public float LyingSpeedModifier = 0.35f, + CrawlingUnderSpeedModifier = 0.5f; [DataField, AutoNetworkedField] public bool AutoGetUp; + /// + /// If true, the entity is choosing to crawl under furniture. This is purely visual and has no effect on physics. + /// [DataField, AutoNetworkedField] - public int NormalDrawDepth = (int) DrawDepth.DrawDepth.Mobs; + public bool IsCrawlingUnder = false; [DataField, AutoNetworkedField] - public int CrawlingDrawDepth = (int) DrawDepth.DrawDepth.SmallMobs; + public int NormalDrawDepth = (int) DrawDepth.DrawDepth.Mobs, + CrawlingUnderDrawDepth = (int) DrawDepth.DrawDepth.SmallMobs; } [Serializable, NetSerializable] @@ -31,15 +35,3 @@ public sealed class CheckAutoGetUpEvent(NetEntity user) : CancellableEntityEvent { public NetEntity User = user; } - -[Serializable, NetSerializable] -public sealed class DrawDownedEvent(NetEntity uid) : EntityEventArgs -{ - public NetEntity Uid = uid; -} - -[Serializable, NetSerializable] -public sealed class DrawStoodEvent(NetEntity uid) : EntityEventArgs -{ - public NetEntity Uid = uid; -} \ No newline at end of file diff --git a/Content.Shared/Standing/SharedLayingDownSystem.cs b/Content.Shared/Standing/SharedLayingDownSystem.cs index bed4ec53bf..914b04cdef 100644 --- a/Content.Shared/Standing/SharedLayingDownSystem.cs +++ b/Content.Shared/Standing/SharedLayingDownSystem.cs @@ -1,10 +1,13 @@ +using Content.Shared.ActionBlocker; +using Content.Shared.CCVar; using Content.Shared.DoAfter; using Content.Shared.Gravity; using Content.Shared.Input; using Content.Shared.Mobs.Systems; using Content.Shared.Movement.Systems; -using Content.Shared.Standing; +using Content.Shared.Popups; using Content.Shared.Stunnable; +using Robust.Shared.Configuration; using Robust.Shared.Input.Binding; using Robust.Shared.Player; using Robust.Shared.Serialization; @@ -17,11 +20,16 @@ public abstract class SharedLayingDownSystem : EntitySystem [Dependency] private readonly StandingStateSystem _standing = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; [Dependency] private readonly SharedGravitySystem _gravity = default!; + [Dependency] private readonly IConfigurationManager _config = default!; + [Dependency] private readonly SharedPopupSystem _popups = default!; + [Dependency] private readonly MovementSpeedModifierSystem _speed = default!; + [Dependency] private readonly ActionBlockerSystem _actionBlocker = default!; public override void Initialize() { CommandBinds.Builder .Bind(ContentKeyFunctions.ToggleStanding, InputCmdHandler.FromDelegate(ToggleStanding)) + .Bind(ContentKeyFunctions.ToggleCrawlingUnder, InputCmdHandler.FromDelegate(HandleCrawlUnderRequest, handle: false)) .Register(); SubscribeNetworkEvent(OnChangeState); @@ -49,17 +57,37 @@ private void ToggleStanding(ICommonSession? session) RaiseNetworkEvent(new ChangeLayingDownEvent()); } + private void HandleCrawlUnderRequest(ICommonSession? session) + { + if (session == null + || session.AttachedEntity is not {} uid + || !TryComp(uid, out var standingState) + || !TryComp(uid, out var layingDown) + || !_actionBlocker.CanInteract(uid, null)) + return; + + var newState = !layingDown.IsCrawlingUnder; + if (standingState.CurrentState is StandingState.Standing) + newState = false; // If the entity is already standing, this function only serves a fallback method to fix its draw depth + + // Do not allow to begin crawling under if it's disabled in config. We still, however, allow to stop it, as a failsafe. + if (newState && !_config.GetCVar(CCVars.CrawlUnderTables)) + { + _popups.PopupEntity(Loc.GetString("crawling-under-tables-disabled-popup"), uid, session); + return; + } + + layingDown.IsCrawlingUnder = newState; + _speed.RefreshMovementSpeedModifiers(uid); + Dirty(uid, layingDown); + } + private void OnChangeState(ChangeLayingDownEvent ev, EntitySessionEventArgs args) { if (!args.SenderSession.AttachedEntity.HasValue) return; var uid = args.SenderSession.AttachedEntity.Value; - - // TODO: Wizard - //if (HasComp(uid)) - // return; - if (!TryComp(uid, out StandingStateComponent? standing) || !TryComp(uid, out LayingDownComponent? layingDown)) return; @@ -89,10 +117,11 @@ private void OnStandingUpDoAfter(EntityUid uid, StandingStateComponent component private void OnRefreshMovementSpeed(EntityUid uid, LayingDownComponent component, RefreshMovementSpeedModifiersEvent args) { - if (_standing.IsDown(uid)) - args.ModifySpeed(component.SpeedModify, component.SpeedModify); - else - args.ModifySpeed(1f, 1f); + if (!_standing.IsDown(uid)) + return; + + var modifier = component.LyingSpeedModifier * (component.IsCrawlingUnder ? component.CrawlingUnderSpeedModifier : 1); + args.ModifySpeed(modifier, modifier); } private void OnParentChanged(EntityUid uid, LayingDownComponent component, EntParentChangedMessage args) @@ -125,6 +154,7 @@ public bool TryStandUp(EntityUid uid, LayingDownComponent? layingDown = null, St return false; standingState.CurrentState = StandingState.GettingUp; + layingDown.IsCrawlingUnder = false; return true; } diff --git a/Content.Shared/Standing/StandingStateSystem.cs b/Content.Shared/Standing/StandingStateSystem.cs index 37f1095fd4..aed6ce372f 100644 --- a/Content.Shared/Standing/StandingStateSystem.cs +++ b/Content.Shared/Standing/StandingStateSystem.cs @@ -1,15 +1,12 @@ using Content.Shared.Buckle; using Content.Shared.Buckle.Components; -using Content.Shared.CCVar; using Content.Shared.Hands.Components; using Content.Shared.Movement.Systems; using Content.Shared.Physics; using Content.Shared.Rotation; using Robust.Shared.Audio.Systems; -using Robust.Shared.Configuration; using Robust.Shared.Physics; using Robust.Shared.Physics.Systems; -using Robust.Shared.Serialization; namespace Content.Shared.Standing; @@ -20,7 +17,6 @@ public sealed class StandingStateSystem : EntitySystem [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly MovementSpeedModifierSystem _movement = default!; [Dependency] private readonly SharedBuckleSystem _buckle = default!; - [Dependency] private readonly IConfigurationManager _config = default!; // If StandingCollisionLayer value is ever changed to more than one layer, the logic needs to be edited. private const int StandingCollisionLayer = (int) CollisionGroup.MidImpassable; @@ -69,10 +65,6 @@ public bool Down(EntityUid uid, bool playSound = true, bool dropHeldItems = true Dirty(standingState); RaiseLocalEvent(uid, new DownedEvent(), false); - // Raising this event will lower the entity's draw depth to the same as a small mob. - if (_config.GetCVar(CCVars.CrawlUnderTables) && setDrawDepth) - RaiseNetworkEvent(new DrawDownedEvent(GetNetEntity(uid))); - // Seemed like the best place to put it _appearance.SetData(uid, RotationVisuals.RotationState, RotationState.Horizontal, appearance); @@ -129,10 +121,6 @@ public bool Stand(EntityUid uid, Dirty(uid, standingState); RaiseLocalEvent(uid, new StoodEvent(), false); - // Raising this event will increase the entity's draw depth to a normal mob's. - if (_config.GetCVar(CCVars.CrawlUnderTables)) - RaiseNetworkEvent(new DrawStoodEvent(GetNetEntity(uid))); - _appearance.SetData(uid, RotationVisuals.RotationState, RotationState.Vertical, appearance); if (TryComp(uid, out FixturesComponent? fixtureComponent)) @@ -170,4 +158,4 @@ public sealed class StoodEvent : EntityEventArgs { } /// /// Raised when an entity is not standing /// -public sealed class DownedEvent : EntityEventArgs { } +public sealed class DownedEvent : EntityEventArgs { } diff --git a/Content.Shared/Traits/Assorted/Components/CyberEyesComponent.cs b/Content.Shared/Traits/Assorted/Components/CyberEyesComponent.cs new file mode 100644 index 0000000000..7009077e9c --- /dev/null +++ b/Content.Shared/Traits/Assorted/Components/CyberEyesComponent.cs @@ -0,0 +1,11 @@ +namespace Content.Shared.Traits.Assorted.Components; + +[RegisterComponent] +public sealed partial class CyberEyesComponent : Component +{ + /// + /// The text that will appear when someone with the CyberEyes component is examined at close range + /// + [DataField] + public string CyberEyesExaminationText = "examine-cybereyes-message"; +} diff --git a/Content.Shared/Traits/Assorted/Systems/CyberEyesSystem.cs b/Content.Shared/Traits/Assorted/Systems/CyberEyesSystem.cs new file mode 100644 index 0000000000..2dde437972 --- /dev/null +++ b/Content.Shared/Traits/Assorted/Systems/CyberEyesSystem.cs @@ -0,0 +1,21 @@ +using Content.Shared.Examine; +using Content.Shared.Traits.Assorted.Components; + +namespace Content.Shared.Traits.Assorted.Systems; + +public sealed class CyberEyesSystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnExamined); + } + + private void OnExamined(EntityUid uid, CyberEyesComponent component, ExaminedEvent args) + { + if (!args.IsInDetailsRange) + return; + + args.PushMarkup($"[color=white]{Loc.GetString(component.CyberEyesExaminationText, ("entity", uid))}[/color]"); + } +} diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 0f1bf764b8..96656ab107 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7165,3 +7165,80 @@ Entries: id: 6439 time: '2024-10-12T20:49:32.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1042 +- author: Fansana + changes: + - type: Fix + message: Fixes the arrivals shuttle choosing to correct docking port. + id: 6440 + time: '2024-10-13T16:38:30.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1040 +- author: FoxxoTrystan + changes: + - type: Fix + message: Oneirophage is once more psionic invisible in webs. + id: 6441 + time: '2024-10-13T18:33:59.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1033 +- author: Mnemotechnician + changes: + - type: Add + message: >- + You can now toggle crawling under furniture! The default keybind is + Shift-R, you can change it in settings. + id: 6442 + time: '2024-10-13T18:34:58.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1036 +- author: DEATHB4DEFEAT + changes: + - type: Tweak + message: Made the show clothing/loadouts button labels more clear + id: 6443 + time: '2024-10-13T18:41:16.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1030 +- author: VMSolidus + changes: + - type: Tweak + message: Brains now can no longer be eaten. + id: 6444 + time: '2024-10-13T18:51:26.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1044 +- author: VMSolidus + changes: + - type: Add + message: >- + Added 8 new Physical Traits. These are, Cyber-Eyes Basic System(Plus 4 + different modular options), Bionic Arm, Dermal Armor, and Platelet + Factories. + id: 6445 + time: '2024-10-13T19:25:05.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1035 +- author: VMSolidus + changes: + - type: Add + message: >- + NT has started hiring contractors from other corporations to fill out + station staff. New loadout items for corporate contractors are now + available for Bartender, Botanist, Cataloguer, Chef, and Janitor. + Currently there are no jumpskirt versions of these uniforms. We are + looking for people willing to make skirt variations of each of these. + id: 6446 + time: '2024-10-13T19:26:23.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1041 +- author: Mnemotechnician + changes: + - type: Add + message: >- + The Courier and Logistics Officer now have a new program in their PDA + for tracking mail delivery performance, including earnings and percent + of packages opened, damaged, or expired. + - type: Add + message: >- + The list of possible mail packages has been greately expanded, and now + includes large parcels. + - type: Add + message: >- + The CourierDrobe now offers a rapid mail delivery device, along with + capsules for it. + id: 6447 + time: '2024-10-13T19:38:19.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1011 diff --git a/Resources/Credits/GitHub.txt b/Resources/Credits/GitHub.txt index aeed963662..3a7e0e7ed6 100644 --- a/Resources/Credits/GitHub.txt +++ b/Resources/Credits/GitHub.txt @@ -1 +1 @@ -0x6273, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 4dplanner, 612git, 778b, Ablankmann, Acruid, actioninja, adamsong, Admiral-Obvious-001, Adrian16199, Aerocrux, Aexxie, africalimedrop, Agoichi, Ahion, AJCM-git, AjexRose, Alekshhh, AlexMorgan3817, AlmondFlour, AlphaQwerty, Altoids1, amylizzle, ancientpower, angelofallars, ArchPigeon, Arendian, arimah, Arteben, AruMoon, as334, AsikKEsel, asperger-sind, aspiringLich, avghdev, AzzyIsNotHere, BananaFlambe, BasedUser, beck-thompson, BGare, BingoJohnson-zz, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, BlueHNT, Boaz1111, BobdaBiscuit, brainfood1183, BramvanZijp, Brandon-Huu, Bribrooo, Bright0, brndd, BubblegumBlue, BYONDFuckery, c4llv07e, CaasGit, CakeQ, CaptainSqrBeard, Carbonhell, Carolyn3114, CatTheSystem, Centronias, chairbender, Charlese2, Cheackraze, cheesePizza2, Chief-Engineer, chromiumboy, Chronophylos, CilliePaint, clorl, Clyybber, CodedCrow, ColdAutumnRain, Colin-Tel, collinlunn, ComicIronic, coolmankid12345, corentt, crazybrain23, creadth, CrigCrag, Crotalus, CrudeWax, CrzyPotato, Cyberboss, d34d10cc, Daemon, daerSeebaer, dahnte, dakamakat, dakimasu, DamianX, DangerRevolution, daniel-cr, Darkenson, DawBla, dch-GH, Deahaka, DEATHB4DEFEAT, DeathCamel58, deathride58, DebugOk, Decappi, Deeeeja, deepdarkdepths, Delete69, deltanedas, DeltaV-Bot, DerbyX, DoctorBeard, DogZeroX, dontbetank, dootythefrooty, Doru991, DoubleRiceEddiedd, DrMelon, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, Duddino, Dutch-VanDerLinde, Easypoller, eclips_e, EEASAS, Efruit, ElectroSR, elthundercloud, Emisse, EmoGarbage404, Endecc, enumerate0, eoineoineoin, ERORR404V1, Errant-4, estacaoespacialpirata, Evgencheg, exincore, exp111, Fahasor, FairlySadPanda, Fansana, ficcialfaint, Fildrance, FillerVK, Fishfish458, Flareguy, FluffiestFloof, FluidRock, FoLoKe, fooberticus, Fortune117, FoxxoTrystan, freeman2651, Froffy025, Fromoriss, FungiFellow, GalacticChimp, gbasood, Geekyhobo, geraeumig, Git-Nivrak, github-actions[bot], gituhabu, gluesniffler, GNF54, Golinth, GoodWheatley, graevy, GreyMario, Guess-My-Name, gusxyz, h3half, Hanzdegloker, Hardly3D, harikattar, HerCoyote23, HoofedEar, Hoolny, hord-brayden, hubismal, Hugal31, Huxellberger, iacore, IamVelcroboy, icekot8, igorsaux, ike709, Illiux, Ilya246, IlyaElDunaev, Injazz, Insineer, Interrobang01, IProduceWidgets, ItsMeThom, Jackal298, Jackrost, jamessimo, janekvap, JerryImMouse, Jessetriesagain, jessicamaybe, Jezithyr, jicksaw, JiimBob, JoeHammad1844, JohnGinnane, johnku1, joshepvodka, jproads, Jrpl, juliangiebel, JustArt1m, JustCone14, JustinTrotter, KaiShibaa, kalane15, kalanosh, Kelrak, kerisargit, keronshb, KIBORG04, Killerqu00, KingFroozy, kira-er, Kit0vras, KittenColony, Ko4ergaPunk, komunre, koteq, Krunklehorn, kxvvv, Lamrr, LankLTE, lapatison, Leander-0, leonardo-dabepis, LetterN, Level10Cybermancer, lever1209, Lgibb18, liltenhead, LittleBuilderJane, Lomcastar, LordCarve, LordEclipse, LovelyLophi, Lukasz825700516, lunarcomets, luringens, lvvova1, lzimann, lzk228, MACMAN2003, Macoron, MagnusCrowe, ManelNavola, Matz05, MehimoNemo, MeltedPixel, MemeProof, Menshin, Mervill, metalgearsloth, mhamsterr, MilenVolf, Minty642, Mirino97, mirrorcult, misandrie, MishaUnity, MisterMecky, Mith-randalf, Mnemotechnician, Moneyl, Moomoobeef, moony, Morb0, Mr0maks, musicmanvr, Myakot, Myctai, N3X15, Nairodian, Naive817, namespace-Memory, NickPowers43, nikthechampiongr, Nimfar11, Nirnael, nmajask, nok-ko, notafet, notquitehadouken, noudoit, nuke-haus, NULL882, nyeogmi, OCOtheOmega, OctoRocket, OldDanceJacket, onoira, osjarw, Owai-Seek, pali6, Pangogie, patrikturi, PaulRitter, Peptide90, peptron1, Phantom-Lily, PHCodes, PixelTheKermit, PJB3005, Plykiya, pofitlo, pointer-to-null, PolterTzi, PoorMansDreams, potato1234x, ProfanedBane, PrPleGoo, ps3moira, Pspritechologist, Psychpsyo, psykzz, PuroSlavKing, quatre, QuietlyWhisper, qwerltaz, Radosvik, Radrark, Rainbeon, Rainfey, Rane, ravage123321, rbertoche, Redict, RedlineTriad, RednoWCirabrab, RemberBM, RemieRichards, RemTim, rene-descartes2021, RiceMar1244, RieBi, Rinkashikachi, Rockdtben, rolfero, rosieposieeee, Saakra, Samsterious, SaphireLattice, ScalyChimp, scrato, Scribbles0, Serkket, ShadowCommander, Shadowtheprotogen546, ShatteredSwords, SignalWalker, SimpleStation14, Simyon264, Sirionaut, siyengar04, Skarletto, Skrauz, Skyedra, SlamBamActionman, slarticodefast, Slava0135, SleepyScarecrow, Snowni, snowsignal, SonicHDC, SoulSloth, SpaceManiac, SpeltIncorrectyl, spoogemonster, ssdaniel24, stalengd, Stealthbomber16, stellar-novas, StrawberryMoses, superjj18, SweptWasTaken, Szunti, TadJohnson00, takemysoult, TaralGit, Tayrtahn, tday93, TekuNut, TemporalOroboros, tentekal, tgrkzus, thatrandomcanadianguy, TheArturZh, theashtronaut, thedraccx, themias, Theomund, theOperand, TheShuEd, TimrodDX, Titian3, tkdrg, Tmanzxd, tmtmtl30, TokenStyle, tom-leys, tomasalves8, Tomeno, Tornado-Technology, tosatur, Tryded, TsjipTsjip, Tunguso4ka, TurboTrackerss14, Tyler-IN, Tyzemol, UbaserB, UKNOWH, UltimateJester, UnicornOnLSD, Uriende, UristMcDorf, Vaaankas, Varen, VasilisThePikachu, veliebm, Veritius, Vermidia, Verslebas, VigersRay, Visne, VMSolidus, volundr-, Voomra, Vordenburg, vulppine, wafehling, WarMechanic, waylon531, weaversam8, whateverusername0, Willhelm53, Winkarst-cpu, wixoaGit, WlarusFromDaSpace, wrexbe, xRiriq, yathxyz, Ygg01, YotaXP, YuriyKiss, zach-hill, Zandario, Zap527, Zealith-Gamer, ZelteHonor, zerorulez, zionnBE, ZNixian, ZoldorfTheWizard, Zumorica, Zymem +0x6273, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 4dplanner, 612git, 778b, Ablankmann, Acruid, actioninja, adamsong, Admiral-Obvious-001, Adrian16199, Aerocrux, Aexxie, africalimedrop, Agoichi, Ahion, AJCM-git, AjexRose, Alekshhh, AlexMorgan3817, AlmondFlour, AlphaQwerty, Altoids1, amylizzle, ancientpower, angelofallars, ArchPigeon, Arendian, arimah, Arteben, AruMoon, as334, AsikKEsel, asperger-sind, aspiringLich, avghdev, AzzyIsNotHere, BananaFlambe, BasedUser, beck-thompson, BGare, BingoJohnson-zz, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, BlueHNT, Boaz1111, BobdaBiscuit, brainfood1183, BramvanZijp, Brandon-Huu, Bribrooo, Bright0, brndd, BubblegumBlue, BYONDFuckery, c4llv07e, CaasGit, CakeQ, CaptainSqrBeard, Carbonhell, Carolyn3114, CatTheSystem, Centronias, chairbender, Charlese2, Cheackraze, cheesePizza2, Chief-Engineer, chromiumboy, Chronophylos, CilliePaint, clorl, Clyybber, CodedCrow, ColdAutumnRain, Colin-Tel, collinlunn, ComicIronic, coolmankid12345, corentt, crazybrain23, creadth, CrigCrag, Crotalus, CrudeWax, CrzyPotato, Cyberboss, d34d10cc, Daemon, daerSeebaer, dahnte, dakamakat, dakimasu, DamianX, DangerRevolution, daniel-cr, Darkenson, DawBla, dch-GH, Deahaka, DEATHB4DEFEAT, DeathCamel58, deathride58, DebugOk, Decappi, Deeeeja, deepdarkdepths, Delete69, deltanedas, DeltaV-Bot, DerbyX, DoctorBeard, DogZeroX, dontbetank, dootythefrooty, Doru991, DoubleRiceEddiedd, DrMelon, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, Duddino, Dutch-VanDerLinde, Easypoller, eclips_e, EEASAS, Efruit, ElectroSR, elthundercloud, Emisse, EmoGarbage404, Endecc, enumerate0, eoineoineoin, ERORR404V1, Errant-4, estacaoespacialpirata, Evgencheg, exincore, exp111, Fahasor, FairlySadPanda, Fansana, ficcialfaint, Fildrance, FillerVK, Fishfish458, Flareguy, FluffiestFloof, FluidRock, FoLoKe, fooberticus, Fortune117, FoxxoTrystan, freeman2651, Froffy025, Fromoriss, FungiFellow, GalacticChimp, gbasood, Geekyhobo, geraeumig, Git-Nivrak, github-actions[bot], gituhabu, gluesniffler, Golinth, GoodWheatley, graevy, GreyMario, Guess-My-Name, gusxyz, h3half, Hanzdegloker, Hardly3D, harikattar, HerCoyote23, HoofedEar, Hoolny, hord-brayden, hubismal, Hugal31, Huxellberger, iacore, IamVelcroboy, icekot8, igorsaux, ike709, Illiux, Ilya246, IlyaElDunaev, Injazz, Insineer, Interrobang01, IProduceWidgets, ItsMeThom, Jackal298, Jackrost, jamessimo, janekvap, JerryImMouse, Jessetriesagain, jessicamaybe, Jezithyr, jicksaw, JiimBob, JoeHammad1844, JohnGinnane, johnku1, joshepvodka, jproads, Jrpl, juliangiebel, JustArt1m, JustCone14, JustinTrotter, KaiShibaa, kalane15, kalanosh, Kelrak, kerisargit, keronshb, KIBORG04, Killerqu00, KingFroozy, kira-er, Kit0vras, KittenColony, Ko4ergaPunk, komunre, koteq, Krunklehorn, kxvvv, Lamrr, LankLTE, lapatison, Leander-0, leonardo-dabepis, LetterN, Level10Cybermancer, lever1209, Lgibb18, liltenhead, LittleBuilderJane, Lomcastar, LordCarve, LordEclipse, LovelyLophi, Lukasz825700516, lunarcomets, luringens, lvvova1, lzimann, lzk228, MACMAN2003, Macoron, MagnusCrowe, ManelNavola, Matz05, MehimoNemo, MeltedPixel, MemeProof, Menshin, Mervill, metalgearsloth, mhamsterr, MilenVolf, Minty642, Mirino97, mirrorcult, misandrie, MishaUnity, MisterMecky, Mith-randalf, Mnemotechnician, Moneyl, Moomoobeef, moony, Morb0, Mr0maks, musicmanvr, Myakot, Myctai, N3X15, Nairodian, Naive817, namespace-Memory, NickPowers43, nikthechampiongr, Nimfar11, Nirnael, nmajask, nok-ko, notafet, notquitehadouken, noudoit, nuke-haus, NULL882, nyeogmi, OCOtheOmega, OctoRocket, OldDanceJacket, onoira, osjarw, Owai-Seek, pali6, Pangogie, patrikturi, PaulRitter, Peptide90, peptron1, Phantom-Lily, PHCodes, PixelTheKermit, PJB3005, Plykiya, pofitlo, pointer-to-null, PolterTzi, PoorMansDreams, potato1234x, ProfanedBane, PrPleGoo, ps3moira, Pspritechologist, Psychpsyo, psykzz, PuroSlavKing, quatre, QuietlyWhisper, qwerltaz, Radosvik, Radrark, Rainbeon, Rainfey, Rane, ravage123321, rbertoche, Redict, RedlineTriad, RednoWCirabrab, RemberBM, RemieRichards, RemTim, rene-descartes2021, RiceMar1244, RieBi, Rinkashikachi, Rockdtben, rolfero, rosieposieeee, Saakra, Samsterious, SaphireLattice, ScalyChimp, scrato, Scribbles0, Serkket, ShadowCommander, Shadowtheprotogen546, ShatteredSwords, SignalWalker, SimpleStation14, Simyon264, Sirionaut, siyengar04, Skarletto, Skrauz, Skyedra, SlamBamActionman, slarticodefast, Slava0135, SleepyScarecrow, Snowni, snowsignal, SonicHDC, SoulSloth, SpaceManiac, SpeltIncorrectyl, spoogemonster, ssdaniel24, stalengd, Stealthbomber16, stellar-novas, StrawberryMoses, superjj18, SweptWasTaken, Szunti, TadJohnson00, takemysoult, TaralGit, Tayrtahn, tday93, TekuNut, TemporalOroboros, tentekal, tgrkzus, thatrandomcanadianguy, TheArturZh, theashtronaut, thedraccx, themias, Theomund, theOperand, TheShuEd, TimrodDX, Titian3, tkdrg, Tmanzxd, tmtmtl30, TokenStyle, tom-leys, tomasalves8, Tomeno, Tornado-Technology, tosatur, Tryded, TsjipTsjip, Tunguso4ka, TurboTrackerss14, Tyler-IN, Tyzemol, UbaserB, UKNOWH, UltimateJester, UnicornOnLSD, Uriende, UristMcDorf, Vaaankas, Varen, VasilisThePikachu, veliebm, Veritius, Vermidia, Verslebas, VigersRay, Visne, VMSolidus, volundr-, Voomra, Vordenburg, vulppine, wafehling, WarMechanic, waylon531, weaversam8, whateverusername0, Willhelm53, Winkarst-cpu, wixoaGit, WlarusFromDaSpace, wrexbe, xRiriq, yathxyz, Ygg01, YotaXP, YuriyKiss, zach-hill, Zandario, Zap527, Zealith-Gamer, zelezniciar1, ZelteHonor, zerorulez, zionnBE, ZNixian, ZoldorfTheWizard, Zumorica, Zymem diff --git a/Resources/Locale/en-US/deltav/cartridge-loader/cartridges.ftl b/Resources/Locale/en-US/deltav/cartridge-loader/cartridges.ftl index 9b4c59d001..ede1a36b8e 100644 --- a/Resources/Locale/en-US/deltav/cartridge-loader/cartridges.ftl +++ b/Resources/Locale/en-US/deltav/cartridge-loader/cartridges.ftl @@ -118,3 +118,16 @@ crime-assist-sophont-explanation = A sophont is described as any entity with the • [bold]Sentience[/bold]: the entity has the capacity to process an emotion or lack thereof, or at a minimum the ability to recognise its own pain. • [bold]Self-awareness[/bold]: the entity is capable of altering its behaviour in a reasonable fashion as a result of stimuli, or at a minimum is capable of recognising its own sapience and sentience. Any sophont is considered a legal person, regardless of origin or prior cognitive status. Much like any other intelligent organic, a sophont may press charges against crew and be tried for crimes. + +mail-metrics-program-name = MailMetrics +mail-metrics-header = Income from Mail Deliveries +mail-metrics-opened = Earnings (Opened) +mail-metrics-expired = Losses (Expired) +mail-metrics-damaged = Losses (Damaged) +mail-metrics-tampered = Losses (Tampered) +mail-metrics-unopened = Unopened +mail-metrics-count-header = Packages +mail-metrics-money-header = Spesos +mail-metrics-total = Total +mail-metrics-progress = {$opened} out of {$total} packages opened! +mail-metrics-progress-percent = Success rate: {$successRate}% diff --git a/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl b/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl index ea24439f70..66e525b8f3 100644 --- a/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl +++ b/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl @@ -147,6 +147,7 @@ ui-options-function-rotate-stored-item = Rotate stored item ui-options-function-offer-item = Offer something ui-options-function-save-item-location = Save item location ui-options-function-toggle-standing = Toggle standing +ui-options-function-toggle-crawling-under = Toggle crawling under furniture ui-options-static-storage-ui = Lock storage window to hotbar ui-options-function-smart-equip-backpack = Smart-equip to backpack diff --git a/Resources/Locale/en-US/loadouts/categories.ftl b/Resources/Locale/en-US/loadouts/categories.ftl index ddabf9db13..193760005e 100644 --- a/Resources/Locale/en-US/loadouts/categories.ftl +++ b/Resources/Locale/en-US/loadouts/categories.ftl @@ -26,6 +26,9 @@ loadout-category-JobsSecurity = Security loadout-category-JobsService = Service loadout-category-JobsServiceUncategorized = Uncategorized loadout-category-JobsServiceBartender = Bartender +loadout-category-JobsServiceBotanist = Botanist +loadout-category-JobsServiceChef = Chef +loadout-category-JobsServiceJanitor = Janitor loadout-category-Mask = Mask loadout-category-Neck = Neck loadout-category-Outer = Outer diff --git a/Resources/Locale/en-US/loadouts/itemgroups.ftl b/Resources/Locale/en-US/loadouts/itemgroups.ftl index 50f37e7c7a..9e66e200bc 100644 --- a/Resources/Locale/en-US/loadouts/itemgroups.ftl +++ b/Resources/Locale/en-US/loadouts/itemgroups.ftl @@ -29,6 +29,19 @@ character-item-group-LoadoutHeadEngineering = Engineering Headgear character-item-group-LoadoutOuterEngineering = Engineering Outerwear character-item-group-LoadoutUniformsEngineering = Engineering Uniforms +# Epistemics +character-item-group-LoadoutEyesScience = Epistemics Eyewear +character-item-group-LoadoutGlovesScience = Epistemics Gloves +character-item-group-LoadoutHeadScience = Epistemics Headgear +character-item-group-LoadoutMaskScience = Epistemics Masks +character-item-group-LoadoutNeckScience = Epistemics Neckwear +character-item-group-LoadoutOuterScience = Epistemics Outerwear +character-item-group-LoadoutShoesScience = Epistemics Shoes +character-item-group-LoadoutUniformsScience = Epistemics Uniforms + +# Epistemics - Cataloguer +character-item-group-LoadoutCataloguerUniforms = Cataloguer Uniforms + # Medical character-item-group-LoadoutEyesMedical = Medical Eyewear character-item-group-LoadoutGlovesMedical = Medical Gloves @@ -55,6 +68,7 @@ character-item-group-LoadoutHoSWeapon = Head of Security's Antique Weapon Collec # Service character-item-group-LoadoutEquipmentService = Service Equipment +character-item-group-LoadoutHeadService = Service Headgear character-item-group-LoadoutMaskService = Service Masks character-item-group-LoadoutNeckService = Service Neckwear character-item-group-LoadoutOuterService = Service Outerwear @@ -63,9 +77,22 @@ character-item-group-LoadoutUniformsService = Service Uniforms # Service - Bartender character-item-group-LoadoutBartenderAmmo = Bartender Ammo +character-item-group-LoadoutBartenderHead = Bartender Headgear character-item-group-LoadoutBartenderOuterwear = Bartender Outerwear +character-item-group-LoadoutBartenderUniforms = Bartender Uniforms character-item-group-LoadoutBartenderWeapon = Bartender Weapon +# Service - Botanist +character-item-group-LoadoutBotanistUniforms = Botanist Uniforms + +# Service - Chef +character-item-group-LoadoutChefHead = Chef Headgear +character-item-group-LoadoutChefOuter = Chef Outerwear +character-item-group-LoadoutChefUniforms = Chef Uniforms + +# Service - Janitor +character-item-group-LoadoutJanitorUniforms = Janitor Uniforms + # Service - Musician character-item-group-LoadoutMusicianInstruments = Musician Instruments diff --git a/Resources/Locale/en-US/Mail/mail.ftl b/Resources/Locale/en-US/mail/commands.ftl similarity index 52% rename from Resources/Locale/en-US/Mail/mail.ftl rename to Resources/Locale/en-US/mail/commands.ftl index 72cd3879b3..1f471bb7a5 100644 --- a/Resources/Locale/en-US/Mail/mail.ftl +++ b/Resources/Locale/en-US/mail/commands.ftl @@ -1,22 +1,6 @@ -mail-recipient-mismatch = Recipient name or job does not match. -mail-invalid-access = Recipient name and job match, but access isn't as expected. -mail-locked = The anti-tamper lock hasn't been removed. Tap the recipient's ID. -mail-desc-far = A parcel of mail. You can't make out who it's addressed to from this distance. -mail-desc-close = A parcel of mail addressed to {CAPITALIZE($name)}, {$job}. -mail-desc-fragile = It has a [color=red]red fragile label[/color]. -mail-desc-priority = The anti-tamper lock's [color=yellow]yellow priority tape[/color] is active. Better deliver it on time! -mail-desc-priority-inactive = The anti-tamper lock's [color=#886600]yellow priority tape[/color] is inactive. -mail-unlocked = Anti-tamper system unlocked. -mail-unlocked-by-emag = Anti-tamper system *BZZT*. -mail-unlocked-reward = Anti-tamper system unlocked. {$bounty} spesos have been added to logistics's account. -mail-penalty-lock = ANTI-TAMPER LOCK BROKEN. LOGISTICS BANK ACCOUNT PENALIZED BY {$credits} CREDITS. -mail-penalty-fragile = INTEGRITY COMPROMISED. LOGISTICS BANK ACCOUNT PENALIZED BY {$credits} CREDITS. -mail-penalty-expired = DELIVERY PAST DUE. LOGISTICS BANK ACCOUNT PENALIZED BY {$credits} CREDITS. -mail-item-name-unaddressed = mail -mail-item-name-addressed = mail ({$recipient}) - +# Mailto command-mailto-description = Queue a parcel to be delivered to an entity. Example usage: `mailto 1234 5678 false false`. The target container's contents will be transferred to an actual mail parcel. -command-mailto-help = Usage: {$command} [is-fragile: true or false] [is-priority: true or false] +command-mailto-help = Usage: {$command} [is-fragile: true or false] [is-priority: true or false] [is-large: true or false, optional] command-mailto-no-mailreceiver = Target recipient entity does not have a {$requiredComponent}. command-mailto-no-blankmail = The {$blankMail} prototype doesn't exist. Something is very wrong. Contact a programmer. command-mailto-bogus-mail = {$blankMail} did not have {$requiredMailComponent}. Something is very wrong. Contact a programmer. @@ -25,6 +9,12 @@ command-mailto-unable-to-receive = Target recipient entity was unable to be setu command-mailto-no-teleporter-found = Target recipient entity was unable to be matched to any station's mail teleporter. Recipient may be off-station. command-mailto-success = Success! Mail parcel has been queued for next teleport in {$timeToTeleport} seconds. +# Mailnow command-mailnow = Force all mail teleporters to deliver another round of mail as soon as possible. This will not bypass the undelivered mail limit. command-mailnow-help = Usage: {$command} command-mailnow-success = Success! All mail teleporters will be delivering another round of mail soon. + +# Mailtestbulk +command-mailtestbulk = Sends one of each type of parcel to a given mail teleporter. Implicitly calls mailnow. +command-mailtestbulk-help = Usage: {$command} +command-mailtestbulk-success = Success! All mail teleporters will be delivering another round of mail soon. diff --git a/Resources/Locale/en-US/mail/mail.ftl b/Resources/Locale/en-US/mail/mail.ftl new file mode 100644 index 0000000000..5a27737732 --- /dev/null +++ b/Resources/Locale/en-US/mail/mail.ftl @@ -0,0 +1,23 @@ +mail-recipient-mismatch = Recipient name or job does not match. +mail-invalid-access = Recipient name and job match, but access isn't as expected. +mail-locked = The anti-tamper lock hasn't been removed. Tap the recipient's ID. +mail-desc-far = A parcel of mail. You can't make out who it's addressed to from this distance. +mail-desc-close = A parcel of mail addressed to {CAPITALIZE($name)}, {$job}. +mail-desc-fragile = It has a [color=red]red fragile label[/color]. +mail-desc-priority = The anti-tamper lock's [color=yellow]yellow priority tape[/color] is active. Better deliver it on time! +mail-desc-priority-inactive = The anti-tamper lock's [color=#886600]yellow priority tape[/color] is inactive. +mail-unlocked = Anti-tamper system unlocked. +mail-unlocked-by-emag = Anti-tamper system *BZZT*. +mail-unlocked-reward = Anti-tamper system unlocked. {$bounty} spesos have been added to logistics' account. +mail-penalty-lock = ANTI-TAMPER LOCK BROKEN. LOGISTICS BANK ACCOUNT PENALIZED BY {$credits} SPESOS. +mail-penalty-fragile = INTEGRITY COMPROMISED. LOGISTICS BANK ACCOUNT PENALIZED BY {$credits} SPESOS. +mail-penalty-expired = DELIVERY PAST DUE. LOGISTICS BANK ACCOUNT PENALIZED BY {$credits} SPESOS. +mail-item-name-unaddressed = mail +mail-item-name-addressed = mail ({$recipient}) + +mail-large-item-name-unaddressed = package +mail-large-item-name-addressed = package ({$recipient}) +mail-large-desc-far = A large package. +mail-large-desc-close = A large package addressed to {CAPITALIZE($name)}, {$job}. + + diff --git a/Resources/Locale/en-US/movement/laying.ftl b/Resources/Locale/en-US/movement/laying.ftl index f75061d6a7..de84ca629e 100644 --- a/Resources/Locale/en-US/movement/laying.ftl +++ b/Resources/Locale/en-US/movement/laying.ftl @@ -1,3 +1,6 @@ +crawling-under-tables-disabled-popup = Crawling under tables is disabled on this server. + +# TODO either remove those, or make use of them laying-comp-lay-success-self = You lay down. laying-comp-lay-success-other = {THE($entity)} lays down. laying-comp-lay-fail-self = You can't lay down right now. diff --git a/Resources/Locale/en-US/preferences/ui/humanoid-profile-editor.ftl b/Resources/Locale/en-US/preferences/ui/humanoid-profile-editor.ftl index c9b7105314..022f3423c0 100644 --- a/Resources/Locale/en-US/preferences/ui/humanoid-profile-editor.ftl +++ b/Resources/Locale/en-US/preferences/ui/humanoid-profile-editor.ftl @@ -2,8 +2,8 @@ humanoid-profile-editor-randomize-everything-button = Randomize everything humanoid-profile-editor-name-label = Name: humanoid-profile-editor-name-random-button = Randomize humanoid-profile-editor-appearance-tab = Appearance -humanoid-profile-editor-clothing = Show clothing -humanoid-profile-editor-loadouts = Show loadout +humanoid-profile-editor-clothing = Preview job equipment: +humanoid-profile-editor-loadouts = Preview loadout items: humanoid-profile-editor-clothing-show = Show humanoid-profile-editor-sex-label = Sex: humanoid-profile-editor-sex-male-text = Male diff --git a/Resources/Locale/en-US/traits/misc.ftl b/Resources/Locale/en-US/traits/misc.ftl new file mode 100644 index 0000000000..b76dfa9729 --- /dev/null +++ b/Resources/Locale/en-US/traits/misc.ftl @@ -0,0 +1 @@ +examine-cybereyes-message = {$entity}'s eyes shine with a faint inner light. diff --git a/Resources/Locale/en-US/traits/traits.ftl b/Resources/Locale/en-US/traits/traits.ftl index 31770a33e4..1c16a94421 100644 --- a/Resources/Locale/en-US/traits/traits.ftl +++ b/Resources/Locale/en-US/traits/traits.ftl @@ -352,4 +352,45 @@ trait-description-HighDampening = trait-name-Azaziba = Sinta'Azaziba trait-description-Azaziba = A language of Moghes consisting of a combination of spoken word and gesticulation. - While waning since Moghes entered the galactic stage - it enjoys popular use by Unathi that never fell to the Hegemony's cultural dominance. \ No newline at end of file + While waning since Moghes entered the galactic stage - it enjoys popular use by Unathi that never fell to the Hegemony's cultural dominance. + +trait-name-BionicArm = Bionic Arm +trait-description-BionicArm = + One or more of your limbs have been replaced with an expensive, state of the art bionic. It could be either one made of highly realistic synthflesh, + or a more obvious metal limb. This limb provides enhanced strength to its user, allowing one to pry open barriers with their bare hands. + +trait-name-PlateletFactories = Platelet Factories +trait-description-PlateletFactories = + Your body has been augmented with a series of biotailored organs that enhance the owner's long term survivability. These organs will attempt + to keep the user alive, even in the face of advanced trauma, all the way up until - but not including - death. + Your natural healing is no longer capped, and will now slowly heal any damage type. This includes more exotic injuries like radiation exposure, or cancer. + +trait-name-DermalArmor = Dermal Armor +trait-description-DermalArmor = + Your skin has been replaced with a flexible, yet sturdy, hard-polymer shell wrapped in a layer of synthetic flesh. + This augmentation provides an innate 10% resistance to physical damage. + +trait-name-CyberEyes = Cyber-Eyes Basic System +trait-description-CyberEyes = + One or more of your eyes have been replaced with a highly modular mechanical ocular implant. + Their most basic functionality is to provide amelioration for weaknesses of the wearer's natural eyes, + but additionally these implants provide protection from bright flashes of light. + +trait-name-CyberEyesSecurity = Cyber-Eyes SecHud +trait-description-CyberEyesSecurity = + Your Cyber-Eyes have been upgraded to include a built-in Security Hud. Note that this augmentation is considered Contraband + for anyone not under the employ of station Security personel, and may be disabled by your employer before dispatch to the station. + +trait-name-CyberEyesMedical = Cyber-Eyes MedHud +trait-description-CyberEyesMedical = + Your Cyber-Eyes have been upgraded to include a built-in Medical Hud, allowing you to track the relative health condition of biological organisms. + +trait-name-CyberEyesDiagnostic = Cyber-Eyes DiagHud +trait-description-CyberEyesDiagnostic = + Your Cyber-Eyes have been upgraded to include a built-in Diagnostic Hud, allowing you to track the condition of synthetic entities. + +trait-name-CyberEyesOmni = Cyber-Eyes HudSuite +trait-description-CyberEyesOmni = + This expensive implant provides the combined benefits of a SecHud, MedHud, and a DiagHud. + Note that this augmentation is considered Contraband for anyone not under the employ of station Security personel, + and may be disabled by your employer before dispatch to the station. diff --git a/Resources/Migrations/frontierMigrations.yml b/Resources/Migrations/frontierMigrations.yml new file mode 100644 index 0000000000..e8d607494e --- /dev/null +++ b/Resources/Migrations/frontierMigrations.yml @@ -0,0 +1,2 @@ +# 2024-08-22 - Frontier Mail - Add more to these when they come up as mapped. Part of the Frontier Mail port, blame Tortuga. +MailPAI: MailNFPAI diff --git a/Resources/Prototypes/Body/Organs/diona.yml b/Resources/Prototypes/Body/Organs/diona.yml index 69fc630b9e..a2133f7f90 100644 --- a/Resources/Prototypes/Body/Organs/diona.yml +++ b/Resources/Prototypes/Body/Organs/diona.yml @@ -27,11 +27,12 @@ - type: entity id: OrganDionaBrain - parent: [BaseDionaOrgan, OrganHumanBrain] + parent: OrganHumanBrain name: brain description: "The source of incredible, unending intelligence. Honk." components: - type: Sprite + sprite: Mobs/Species/Diona/organs.rsi state: brain - type: SolutionContainerManager solutions: @@ -102,7 +103,7 @@ layers: - state: lung-l - state: lung-r - - type: Lung + - type: Lung - type: Metabolizer removeEmpty: true solutionOnBody: false @@ -131,7 +132,7 @@ description: "The source of incredible, unending intelligence. Honk." components: - type: Brain - - type: Nymph # This will make the organs turn into a nymph when they're removed. + - type: Nymph # This will make the organs turn into a nymph when they're removed. entityPrototype: OrganDionaNymphBrain transferMind: true @@ -170,11 +171,11 @@ - type: entity id: OrganDionaNymphStomach - parent: MobDionaNymphAccent + parent: MobDionaNymphAccent noSpawn: true name: diona nymph suffix: Stomach - description: Contains the stomach of a formerly fully-formed Diona. It doesn't taste any better for it. + description: Contains the stomach of a formerly fully-formed Diona. It doesn't taste any better for it. components: - type: IsDeadIC - type: Body @@ -186,7 +187,7 @@ noSpawn: true name: diona nymph suffix: Lungs - description: Contains the lungs of a formerly fully-formed Diona. Breathtaking. + description: Contains the lungs of a formerly fully-formed Diona. Breathtaking. components: - type: IsDeadIC - type: Body diff --git a/Resources/Prototypes/Body/Organs/human.yml b/Resources/Prototypes/Body/Organs/human.yml index 69081020ce..e6a04d4a60 100644 --- a/Resources/Prototypes/Body/Organs/human.yml +++ b/Resources/Prototypes/Body/Organs/human.yml @@ -6,6 +6,13 @@ - type: Sprite sprite: Mobs/Species/Human/organs.rsi - type: Organ + +- type: entity + id: BaseHumanOrgan + parent: BaseHumanOrganUnGibbable + abstract: true + components: + - type: Gibbable - type: Food - type: Extractable grindableSolutionName: organ @@ -27,13 +34,6 @@ tags: - Meat -- type: entity - id: BaseHumanOrgan - parent: BaseHumanOrganUnGibbable - abstract: true - components: - - type: Gibbable - - type: entity id: OrganHumanBrain parent: BaseHumanOrganUnGibbable @@ -67,7 +67,7 @@ - type: FlavorProfile flavors: - people - + - type: entity id: OrganHumanEyes parent: BaseHumanOrgan diff --git a/Resources/Prototypes/Body/Organs/slime.yml b/Resources/Prototypes/Body/Organs/slime.yml index 3da76c5d4a..5b908e75f4 100644 --- a/Resources/Prototypes/Body/Organs/slime.yml +++ b/Resources/Prototypes/Body/Organs/slime.yml @@ -1,6 +1,6 @@ - type: entity id: SentientSlimeCore - parent: [BaseItem, OrganHumanBrain] + parent: OrganHumanBrain name: sentient slime core description: "The source of incredible, unending gooeyness." components: @@ -34,7 +34,7 @@ - ReagentId: Slime Quantity: 10 - + - type: entity id: OrganSlimeLungs parent: BaseHumanOrgan diff --git a/Resources/Prototypes/CharacterItemGroups/scienceGroups.yml b/Resources/Prototypes/CharacterItemGroups/scienceGroups.yml index e5e43760da..ac41a9286a 100644 --- a/Resources/Prototypes/CharacterItemGroups/scienceGroups.yml +++ b/Resources/Prototypes/CharacterItemGroups/scienceGroups.yml @@ -101,3 +101,22 @@ items: - type: loadout id: LoadoutScienceShoesBootsWinterSci + +# Cataloguer +- type: characterItemGroup + id: LoadoutCataloguerUniforms + items: + - type: loadout + id: LoadoutScienceJumpsuitLibrarianNt + - type: loadout + id: LoadoutScienceJumpsuitLibrarianIdris + - type: loadout + id: LoadoutScienceJumpsuitLibrarianOrion + - type: loadout + id: LoadoutScienceJumpsuitLibrarianHeph + - type: loadout + id: LoadoutScienceJumpsuitLibrarianPMCG + - type: loadout + id: LoadoutScienceJumpsuitLibrarianZav + - type: loadout + id: LoadoutScienceJumpsuitLibrarianZeng \ No newline at end of file diff --git a/Resources/Prototypes/CharacterItemGroups/serviceGroups.yml b/Resources/Prototypes/CharacterItemGroups/serviceGroups.yml index 01ec0aaeea..61c2b286b7 100644 --- a/Resources/Prototypes/CharacterItemGroups/serviceGroups.yml +++ b/Resources/Prototypes/CharacterItemGroups/serviceGroups.yml @@ -76,12 +76,24 @@ - type: loadout id: LoadoutServiceClownCowToolboxFilled +- type: characterItemGroup + id: LoadoutHeadService + items: + - type: loadout + id: LoadoutServiceClownCowToolboxFilled + # Bartender - type: characterItemGroup id: LoadoutBartenderOuterwear items: - type: loadout id: LoadoutServiceBartenderArmorDuraVest + - type: loadout + id: LoadoutServiceOuterBartenderNt + - type: loadout + id: LoadoutServiceOuterBartenderIdris + - type: loadout + id: LoadoutServiceOuterBartenderOrion - type: characterItemGroup id: LoadoutBartenderAmmo @@ -98,3 +110,76 @@ id: LoadoutServiceBartenderShotgunDoubleBarreledRubber - type: loadout id: LoadoutServiceBartenderMosinRubber + +- type: characterItemGroup + id: LoadoutBartenderUniforms + items: + - type: loadout + id: LoadoutServiceJumpsuitBartenderNt + - type: loadout + id: LoadoutServiceJumpsuitBartenderIdris + - type: loadout + id: LoadoutServiceJumpsuitBartenderOrion + +- type: characterItemGroup + id: LoadoutBartenderHead + items: + - type: loadout + id: LoadoutServiceHeadBartenderNt + - type: loadout + id: LoadoutServiceHeadBartenderIdris + - type: loadout + id: LoadoutServiceHeadBartenderOrion + +# Botanist +- type: characterItemGroup + id: LoadoutBotanistUniforms + items: + - type: loadout + id: LoadoutServiceJumpsuitHydroponicsNt + - type: loadout + id: LoadoutServiceJumpsuitHydroponicsIdris + - type: loadout + id: LoadoutServiceJumpsuitHydroponicsOrion + +# Chef +- type: characterItemGroup + id: LoadoutChefUniforms + items: + - type: loadout + id: LoadoutServiceJumpsuitChefNt + - type: loadout + id: LoadoutServiceJumpsuitChefIdris + - type: loadout + id: LoadoutServiceJumpsuitChefOrion + +- type: characterItemGroup + id: LoadoutChefHead + items: + - type: loadout + id: LoadoutServiceHeadChefNt + - type: loadout + id: LoadoutServiceHeadChefIdris + - type: loadout + id: LoadoutServiceHeadChefOrion + +- type: characterItemGroup + id: LoadoutChefOuter + items: + - type: loadout + id: LoadoutServiceOuterChefNt + - type: loadout + id: LoadoutServiceOuterChefIdris + - type: loadout + id: LoadoutServiceOuterChefOrion + +# Janitor +- type: characterItemGroup + id: LoadoutJanitorUniforms + items: + - type: loadout + id: LoadoutServiceJumpsuitJanitorNt + - type: loadout + id: LoadoutServiceJumpsuitJanitorIdris + - type: loadout + id: LoadoutServiceJumpsuitJanitorOrion \ No newline at end of file diff --git a/Resources/Prototypes/Damage/modifier_sets.yml b/Resources/Prototypes/Damage/modifier_sets.yml index 811d5a580c..30721d5e42 100644 --- a/Resources/Prototypes/Damage/modifier_sets.yml +++ b/Resources/Prototypes/Damage/modifier_sets.yml @@ -359,3 +359,10 @@ Slash: 0.6 Piercing: 0.6 Holy: 1.5 + +- type: damageModifierSet + id: DermalArmor + coefficients: + Blunt: 0.9 + Slash: 0.9 + Piercing: 0.9 diff --git a/Resources/Prototypes/DeltaV/Catalog/VendingMachines/Inventories/courierdrobe.yml b/Resources/Prototypes/DeltaV/Catalog/VendingMachines/Inventories/courierdrobe.yml index 88f691ba9b..778b372585 100644 --- a/Resources/Prototypes/DeltaV/Catalog/VendingMachines/Inventories/courierdrobe.yml +++ b/Resources/Prototypes/DeltaV/Catalog/VendingMachines/Inventories/courierdrobe.yml @@ -7,8 +7,10 @@ CourierBag: 2 ClothingHeadsetCargo: 2 ClothingOuterWinterCargo: 2 - ClothingUniformMailCarrier: 2 # Nyanotrasen - Mail Carrier old gear - ClothingUniformSkirtMailCarrier: 2 # Nyanotrasen - Mail Carrier old gear - ClothingHeadMailCarrier: 2 # Nyanotrasen - Mail Carrier old gear - MailBag: 2 # Nyanotrasen - Mail Carrier old gear - ClothingOuterWinterCoatMail: 2 # Nyanotrasen - Mail Carrier old gear + ClothingUniformMailCarrier: 2 + ClothingUniformSkirtMailCarrier: 2 + ClothingHeadMailCarrier: 2 + MailBag: 2 + ClothingOuterWinterCoatMail: 2 + WeaponMailLake: 2 + BoxMailCapsulePrimed: 4 diff --git a/Resources/Prototypes/DeltaV/Entities/Objects/Devices/cartridges.yml b/Resources/Prototypes/DeltaV/Entities/Objects/Devices/cartridges.yml index def215cee4..81e11d9d08 100644 --- a/Resources/Prototypes/DeltaV/Entities/Objects/Devices/cartridges.yml +++ b/Resources/Prototypes/DeltaV/Entities/Objects/Devices/cartridges.yml @@ -38,3 +38,24 @@ sprite: Objects/Weapons/Melee/stunbaton.rsi state: stunbaton_on - type: SecWatchCartridge + +- type: entity + parent: BaseItem + id: MailMetricsCartridge + name: mail metrics cartridge + description: A cartridge that tracks statistics related to mail deliveries. + components: + - type: Sprite + sprite: DeltaV/Objects/Devices/cartridge.rsi + state: cart-mail + - type: Icon + sprite: DeltaV/Objects/Devices/cartridge.rsi + state: cart-mail + - type: UIFragment + ui: !type:MailMetricUi + - type: MailMetricsCartridge + - type: Cartridge + programName: mail-metrics-program-name + icon: + sprite: Objects/Specific/Mail/mail.rsi + state: icon diff --git a/Resources/Prototypes/DeltaV/Entities/Objects/Specific/Mail/mail.yml b/Resources/Prototypes/DeltaV/Entities/Objects/Specific/Mail/mail.yml index 7de554f5ff..6f96930078 100644 --- a/Resources/Prototypes/DeltaV/Entities/Objects/Specific/Mail/mail.yml +++ b/Resources/Prototypes/DeltaV/Entities/Objects/Specific/Mail/mail.yml @@ -1,3 +1,4 @@ +# DeltaV Mail - type: entity noSpawn: true parent: BaseMail @@ -175,3 +176,1643 @@ - type: Mail contents: - id: FoodPiePumpkin + +- type: entity + noSpawn: true + parent: BaseMail + id: MailDVCosplayFakeWizard + suffix: cosplay-wizard, fake as fuck + components: + - type: Mail + contents: + - id: ClothingOuterWizardFake + - id: ClothingHeadHatWizardFake + - id: ClothingShoesWizardFake + - id: FoodBurgerSpell + orGroup: FakeWizard + prob: 0.3 + - id: FoodKebabSkewer + orGroup: FakeWizard + prob: 0.1 + +- type: entity + parent: BaseMail + id: MailDVScarves + suffix: scarves + components: + - type: Mail + contents: + - id: ClothingNeckScarfStripedRed + orGroup: Scarf + - id: ClothingNeckScarfStripedBlue + orGroup: Scarf + - id: ClothingNeckScarfStripedGreen + orGroup: Scarf + - id: ClothingNeckScarfStripedBlack + orGroup: Scarf + - id: ClothingNeckScarfStripedBrown + orGroup: Scarf + - id: ClothingNeckScarfStripedLightBlue + orGroup: Scarf + - id: ClothingNeckScarfStripedOrange + orGroup: Scarf + - id: ClothingNeckScarfStripedPurple + orGroup: Scarf + - id: ClothingNeckScarfStripedZebra + orGroup: Scarf + - id: ClothingNeckScarfStripedSyndieGreen + orGroup: Scarf + prob: 0.25 + - id: ClothingNeckScarfStripedSyndieRed + orGroup: Scarf + prob: 0.25 + - id: ClothingNeckScarfStripedCentcom + orGroup: Scarf + prob: 0.001 + +- type: entity + parent: BaseMailLarge + id: MailDVBoxes + suffix: boxes + components: + - type: Mail + contents: +# - id: BoxEnvelope # TODO: we do not have this on EE? +# orGroup: Box + - id: BoxCardboard + orGroup: Box + - id: BoxMRE + orGroup: Box + - id: BoxPerformer + orGroup: Box + - id: BoxFlare + orGroup: Box + - id: BoxDarts + orGroup: Box + - id: BoxMousetrap + orGroup: Box + + + +# Frontier Mail, including Extended Nyano Mail. (Pony Express?) +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFAlcohol + suffix: alcohol, extended + components: + - type: Mail + isFragile: true + contents: + # 12.5 overall weight, 8% base chance + - id: DrinkAbsintheBottleFull + orGroup: Drink + - id: DrinkBlueCuracaoBottleFull + orGroup: Drink + - id: DrinkCoffeeLiqueurBottleFull + orGroup: Drink + - id: DrinkGinBottleFull + orGroup: Drink + - id: DrinkMelonLiquorBottleFull + orGroup: Drink + - id: DrinkRumBottleFull + orGroup: Drink + - id: DrinkTequilaBottleFull + orGroup: Drink + - id: DrinkVermouthBottleFull + orGroup: Drink + - id: DrinkVodkaBottleFull + orGroup: Drink + - id: DrinkWhiskeyBottleFull + orGroup: Drink + - id: DrinkWineBottleFull + orGroup: Drink + - id: DrinkChampagneBottleFull + orGroup: Drink + prob: 0.5 + - id: DrinkGildlagerBottleFull + orGroup: Drink + prob: 0.5 + - id: DrinkPatronBottleFull + orGroup: Drink + prob: 0.5 + - id: DrinkGlass + amount: 2 + +- type: entity + noSpawn: true + parent: BaseMailLarge + id: MailNFBible + suffix: bible, extended + components: + - type: Mail + contents: + - id: Bible + - id: ClothingHeadHatWitch1 #DeltaV - Compensates for items that don't exist here + - id: ClothingOuterCoatMNKBlackTopCoat #DeltaV - Compensates for items that don't exist here + +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFBikeHorn + suffix: bike horn, random + components: + - type: Mail + contents: + - id: BikeHorn + orGroup: Horn + prob: 0.95 + - id: CluwneHorn + orGroup: Horn + prob: 0.05 + +- type: entity + noSpawn: true + parent: BaseMailLarge + id: MailNFBuildABuddy + suffix: Build-a-Buddy + components: + - type: Mail + isFragile: true + contents: +# - id: BoxBuildABuddyGoblin # DeltaV - Goblins Aren't Real +# orGroup: Box + - id: BoxBuildABuddyHuman + orGroup: Box + - id: BoxBuildABuddyReptilian + orGroup: Box + - id: BoxBuildABuddySlime + orGroup: Box + - id: BoxBuildABuddyVulpkanin + orGroup: Box + - id: DrinkSpaceGlue + - id: PaperMailNFBuildABuddy + +- type: entity + noSpawn: true + parent: BaseMailLarge + id: MailNFCake + suffix: cake, extended + components: + - type: Mail + isFragile: true + isPriority: true + contents: + # 14.8 total weight, ~6.8% base chance + - id: FoodCakeApple + orGroup: Cake + - id: FoodCakeBirthday + orGroup: Cake + - id: FoodCakeBlueberry + orGroup: Cake + - id: FoodCakeCarrot + orGroup: Cake + - id: FoodCakeCheese + orGroup: Cake + - id: FoodCakeChocolate + orGroup: Cake + - id: FoodCakeChristmas + orGroup: Cake + - id: FoodCakeClown + orGroup: Cake + - id: FoodCakeLemon + orGroup: Cake + - id: FoodCakeLime + orGroup: Cake + - id: FoodCakeOrange + orGroup: Cake + - id: FoodCakePumpkin + orGroup: Cake + - id: FoodCakeVanilla + orGroup: Cake + # Uncommon + - id: FoodMothMothmallow + orGroup: Cake + prob: 0.5 + - id: FoodCakeSlime + orGroup: Cake + prob: 0.5 + # Rare + - id: FoodCakeBrain + orGroup: Cake + prob: 0.25 + - id: FoodCakeLemoon + orGroup: Cake + prob: 0.25 + - id: FoodCakeSuppermatter + orGroup: Cake + prob: 0.25 + # Ultra rare + - id: FoodCakeSpaceman + orGroup: Cake + prob: 0.05 + - id: KnifePlastic + - id: ForkPlastic + amount: 2 + +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFCosplayWizard + suffix: cosplay-wizard, extended + components: + - type: Mail + contents: + - id: ClothingOuterWizard + - id: ClothingHeadHatWizard + - id: ClothingShoesWizard + - id: PonderingOrb + orGroup: Staff + prob: 0.3 + - id: RGBStaff + orGroup: Staff + prob: 0.1 + +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFCosplayMaid + suffix: cosplay-maid, extended + components: + - type: Mail + contents: + - id: UniformMaid + orGroup: Uniform + - id: ClothingUniformJumpskirtJanimaid + orGroup: Uniform + - id: ClothingUniformJumpskirtJanimaidmini + orGroup: Uniform + - id: MegaSprayBottle + - id: ClothingHandsGlovesColorWhite + +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFCosplayNurse + suffix: cosplay-nurse, extended + components: + - type: Mail + contents: + - id: ClothingUniformJumpskirtNurse + - id: ClothingHeadNurseHat + - id: Syringe + +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFCheese + suffix: cheese, extended + components: + - type: Mail + isFragile: true + isPriority: true + contents: + - id: FoodCheese + orGroup: Cheese + prob: 0.5 + - id: FoodChevre + orGroup: Cheese + prob: 0.5 + - id: KnifePlastic + +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFCigarettes + suffix: cigs, random + components: + - type: Mail + contents: + - id: CigPackBlack + orGroup: Cigs + prob: 0.19 + - id: CigPackBlue + orGroup: Cigs + prob: 0.19 + - id: CigPackGreen + orGroup: Cigs + prob: 0.19 + - id: CigPackRed + orGroup: Cigs + prob: 0.19 + - id: CigPackMixed + orGroup: Cigs + prob: 0.09 # Pool shared with CigPackMixedMedical, CigPackMixedNasty + - id: CigPackMixedMedical + orGroup: Cigs + prob: 0.05 + - id: CigPackMixedNasty + orGroup: Cigs + prob: 0.05 + # Rare + - id: CigPackSyndicate + orGroup: Cigs + prob: 0.05 + - id: CheapLighter + +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFEMP + suffix: emp + components: + - type: Mail + contents: + - id: DelayedEMP + - id: PaperMailNFEMPPreparedness + +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFSmoke + suffix: smoke + components: + - type: Mail + contents: + - id: DelayedSmoke + +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFGoldCigars + suffix: cigars, premium + components: + - type: Mail + contents: + - id: CigarGoldCase + orGroup: Cigars + - id: FlippoLighter + orGroup: Lighter + prob: 0.95 + +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFCookies + suffix: cookies, random + components: + - type: Mail + isPriority: true + contents: + # Cookie 1 + - id: FoodBakedCookie + orGroup: Cookie1 + - id: FoodBakedCookieOatmeal + orGroup: Cookie1 + - id: FoodBakedCookieRaisin + orGroup: Cookie1 + - id: FoodBakedCookieSugar + orGroup: Cookie1 + # Cookie 2 + - id: FoodBakedCookie + orGroup: Cookie2 + - id: FoodBakedCookieOatmeal + orGroup: Cookie2 + - id: FoodBakedCookieRaisin + orGroup: Cookie2 + - id: FoodBakedCookieSugar + orGroup: Cookie2 + # Cookie 3 + - id: FoodBakedCookie + orGroup: Cookie3 + - id: FoodBakedCookieOatmeal + orGroup: Cookie3 + - id: FoodBakedCookieRaisin + orGroup: Cookie3 + - id: FoodBakedCookieSugar + orGroup: Cookie3 + # Cookie 4 + - id: FoodBakedCookie + orGroup: Cookie4 + - id: FoodBakedCookieOatmeal + orGroup: Cookie4 + - id: FoodBakedCookieRaisin + orGroup: Cookie4 + - id: FoodBakedCookieSugar + orGroup: Cookie4 + +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFKnife + suffix: knife, extended + components: + - type: Mail + contents: + - id: KukriKnife + orGroup: Knife + - id: Machete # A little large for an envelope but "we'll live" + orGroup: Knife + - id: CombatKnife + orGroup: Knife + - id: SurvivalKnife + orGroup: Knife + +- type: entity + noSpawn: true + parent: BaseMailLarge + id: MailNFSword + suffix: sword + components: + - type: Mail + contents: + - id: KatanaDulled + orGroup: Sword + prob: 0.95 + - id: ClaymoreDulled + orGroup: Sword + prob: 0.95 + - id: FoamCutlass + orGroup: Sword + prob: 0.95 + - id: Katana + orGroup: Sword + prob: 0.5 + - id: Claymore + orGroup: Sword + prob: 0.5 +# - id: CaneSheathFilled # TODO: don't have this yet +# orGroup: Sword +# prob: 0.3 + - id: Cutlass + orGroup: Sword + prob: 0.1 + - id: Kanabou # ah yes, swords + orGroup: Sword + prob: 0.1 + - id: ClothingBeltSheathFilled # Little dangerous between the reflect and the damage + orGroup: Sword + prob: 0.001 + +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFMuffins + suffix: muffins, random + components: + - type: Mail + isPriority: true + contents: + # Muffin 1 + - id: FoodBakedMuffinBerry + orGroup: Muffin1 + - id: FoodBakedMuffinCherry + orGroup: Muffin1 + - id: FoodBakedMuffinBluecherry + orGroup: Muffin1 + - id: FoodBakedMuffin + orGroup: Muffin1 + - id: FoodMothMoffin + orGroup: Muffin1 + prob: 0.5 + # Muffin 2 + - id: FoodBakedMuffinBerry + orGroup: Muffin2 + - id: FoodBakedMuffinCherry + orGroup: Muffin2 + - id: FoodBakedMuffinBluecherry + orGroup: Muffin2 + - id: FoodBakedMuffin + orGroup: Muffin2 + - id: FoodMothMoffin + orGroup: Muffin2 + prob: 0.5 + # Muffin 3 + - id: FoodBakedMuffinBerry + orGroup: Muffin3 + - id: FoodBakedMuffinCherry + orGroup: Muffin3 + - id: FoodBakedMuffinBluecherry + orGroup: Muffin3 + - id: FoodBakedMuffin + orGroup: Muffin3 + - id: FoodMothMoffin + orGroup: Muffin3 + prob: 0.5 + # Muffin 4 + - id: FoodBakedMuffinBerry + orGroup: Muffin4 + - id: FoodBakedMuffinCherry + orGroup: Muffin4 + - id: FoodBakedMuffinBluecherry + orGroup: Muffin4 + - id: FoodBakedMuffin + orGroup: Muffin4 + - id: FoodMothMoffin + orGroup: Muffin4 + prob: 0.5 + +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFPAI + suffix: PAI, extended + components: + - type: Mail + contents: + - id: PersonalAI + orGroup: PAI + prob: 0.99 + # Ultra rare + - id: SyndicatePersonalAI + orGroup: PAI + prob: 0.01 + +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFPlushie + suffix: plushie, extended + components: + - type: Mail + contents: + # These are all grouped up now to guarantee at least one item received. + # The downside is you're not going to get half a dozen plushies anymore. + - id: PlushieBee + orGroup: Plushie + - id: PlushieRGBee + prob: 0.5 + orGroup: Plushie + - id: PlushieNuke + orGroup: Plushie + - id: PlushieArachind #DeltaV - Adds MORE PLUSHIE + orGroup: Plushie + - id: PlushieAtmosian #DeltaV - Adds MORE PLUSHIE + orGroup: Plushie + - id: PlushieXeno #DeltaV - Adds MORE PLUSHIE + orGroup: Plushie + - id: PlushiePenguin #DeltaV - Adds MORE PLUSHIE + orGroup: Plushie + - id: PlushieGhost #DeltaV - Adds MORE PLUSHIE + orGroup: Plushie + - id: PlushieDiona #DeltaV - Adds MORE PLUSHIE + orGroup: Plushie + - id: ToyMouse #DeltaV - Adds MORE PLUSHIE + orGroup: Plushie + - id: PlushieRouny + orGroup: Plushie + - id: PlushieLizard + orGroup: Plushie + - id: PlushieSpaceLizard + orGroup: Plushie + - id: PlushieRatvar + orGroup: Plushie + - id: PlushieNar + orGroup: Plushie + - id: PlushieCarp + orGroup: Plushie + - id: PlushieHolocarp #DeltaV - Adds MORE PLUSHIE + orGroup: Plushie + - id: PlushieRainbowCarp #DeltaV - Adds MORE PLUSHIE + orGroup: Plushie + - id: PlushieSlime + orGroup: Plushie + - id: PlushieSnake + orGroup: Plushie + - id: PlushieMothRandom + orGroup: Plushie + - id: PlushieMoth + prob: 0.5 + orGroup: Plushie + - id: PlushieMothMusician + prob: 0.5 + orGroup: Plushie + - id: PlushieMothBartender + prob: 0.5 + orGroup: Plushie + +# Random snacks, replaces MailChocolate (lousy animal organs) +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFSnacks + suffix: snacks, random + components: + - type: Mail + contents: + # Snack 1 + - id: FoodSnackChocolate + orGroup: Snack1 + - id: FoodSnackPopcorn + orGroup: Snack1 + - id: FoodSnackChips + orGroup: Snack1 + - id: FoodSnackBoritos + orGroup: Snack1 + - id: FoodSnackSus + orGroup: Snack1 + - id: FoodSnackPistachios + orGroup: Snack1 + - id: FoodSnackRaisins + orGroup: Snack1 + - id: FoodSnackCheesie + orGroup: Snack1 + - id: FoodSnackEnergy + orGroup: Snack1 + - id: FoodSnackCnDs + orGroup: Snack1 + - id: FoodSnackSemki + orGroup: Snack1 + - id: FoodSnackSyndi + orGroup: Snack1 + prob: 0.5 + # Snack 2 + - id: FoodSnackChocolate + orGroup: Snack2 + - id: FoodSnackPopcorn + orGroup: Snack2 + - id: FoodSnackChips + orGroup: Snack2 + - id: FoodSnackBoritos + orGroup: Snack2 + - id: FoodSnackSus + orGroup: Snack2 + - id: FoodSnackPistachios + orGroup: Snack2 + - id: FoodSnackRaisins + orGroup: Snack2 + - id: FoodSnackCheesie + orGroup: Snack2 + - id: FoodSnackEnergy + orGroup: Snack2 + - id: FoodSnackCnDs + orGroup: Snack2 + - id: FoodSnackSemki + orGroup: Snack2 + - id: FoodSnackSyndi + orGroup: Snack2 + prob: 0.5 + # Snack 3 + - id: FoodSnackChocolate + orGroup: Snack3 + - id: FoodSnackPopcorn + orGroup: Snack3 + - id: FoodSnackChips + orGroup: Snack3 + - id: FoodSnackBoritos + orGroup: Snack3 + - id: FoodSnackSus + orGroup: Snack3 + - id: FoodSnackPistachios + orGroup: Snack3 + - id: FoodSnackRaisins + orGroup: Snack3 + - id: FoodSnackCheesie + orGroup: Snack3 + - id: FoodSnackEnergy + orGroup: Snack3 + - id: FoodSnackCnDs + orGroup: Snack3 + - id: FoodSnackSemki + orGroup: Snack3 + - id: FoodSnackSyndi + orGroup: Snack3 + prob: 0.5 + # Snack 4 + - id: FoodSnackChocolate + orGroup: Snack4 + - id: FoodSnackPopcorn + orGroup: Snack4 + - id: FoodSnackChips + orGroup: Snack4 + - id: FoodSnackBoritos + orGroup: Snack4 + - id: FoodSnackSus + orGroup: Snack4 + - id: FoodSnackPistachios + orGroup: Snack4 + - id: FoodSnackRaisins + orGroup: Snack4 + - id: FoodSnackCheesie + orGroup: Snack4 + - id: FoodSnackEnergy + orGroup: Snack4 + - id: FoodSnackCnDs + orGroup: Snack4 + - id: FoodSnackSemki + orGroup: Snack4 + - id: FoodSnackSyndi + orGroup: Snack4 + prob: 0.5 + +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFVagueThreat + suffix: vague-threat + components: + - type: Mail + contents: + - id: PaperMailNFVagueThreat1 + orGroup: Paper + - id: PaperMailNFVagueThreat2 + orGroup: Paper + - id: PaperMailNFVagueThreat3 + orGroup: Paper + - id: PaperMailNFVagueThreat4 + orGroup: Paper + - id: PaperMailNFVagueThreat5 + orGroup: Paper + - id: PaperMailNFVagueThreat6 + orGroup: Paper + - id: PaperMailNFVagueThreat7 + orGroup: Paper + - id: PaperMailNFVagueThreat8 + orGroup: Paper + - id: PaperMailNFVagueThreat9 + orGroup: Paper + - id: PaperMailNFVagueThreat10 + orGroup: Paper + - id: PaperMailNFVagueThreat11 + orGroup: Paper + - id: PaperMailNFVagueThreat12 + orGroup: Paper + - id: KitchenKnife + orGroup: ThreateningObject + - id: ButchCleaver + orGroup: ThreateningObject + - id: CombatKnife + orGroup: ThreateningObject + - id: SurvivalKnife + orGroup: ThreateningObject + - id: SoapHomemade + orGroup: ThreateningObject + - id: FoodMeat + orGroup: ThreateningObject + - id: OrganHumanHeart + orGroup: ThreateningObject + +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFDonkPockets + suffix: donk pockets, random + components: + - type: Mail + contents: + - id: FoodBoxDonkpocket + orGroup: Donk + prob: 0.4 # Higher probability, useful for chefs. + - id: FoodBoxDonkpocketSpicy + orGroup: Donk + prob: 0.1 + - id: FoodBoxDonkpocketTeriyaki + orGroup: Donk + prob: 0.1 + - id: FoodBoxDonkpocketPizza + orGroup: Donk + prob: 0.1 + - id: FoodBoxDonkpocketStonk + orGroup: Donk + prob: 0.05 + - id: FoodBoxDonkpocketCarp + orGroup: Donk + prob: 0.05 + - id: FoodBoxDonkpocketBerry + orGroup: Donk + prob: 0.1 + - id: FoodBoxDonkpocketHonk + orGroup: Donk + prob: 0.05 + - id: FoodBoxDonkpocketDink + orGroup: Donk + prob: 0.05 # Bad luck. + +# Needs a buff? +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFSodaPwrGame + suffix: Pwrgame + components: + - type: Mail + contents: + - id: DrinkPwrGameCan + amount: 3 + - id: PaperMailNFPwrGameAd + +# Needs a buff? +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFSodaRedBool + suffix: Red Bool + components: + - type: Mail + contents: + - id: DrinkEnergyDrinkCan + amount: 3 + - id: PaperMailNFRedBoolAd + +# Needs a buff? +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFSodaSpaceCola + suffix: Space Cola + components: + - type: Mail + contents: + - id: DrinkColaBottleFull + - id: PaperMailNFSpaceColaAd + +# Needs a buff? +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFSodaSpaceMountainWind + suffix: Space Mountain Wind + components: + - type: Mail + contents: + - id: DrinkSpaceMountainWindBottleFull + - id: PaperMailNFSpaceMountainWindAd + +# Needs a buff? +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFSodaSpaceUp + suffix: Space Up + components: + - type: Mail + contents: + - id: DrinkSpaceUpBottleFull + - id: PaperMailNFSpaceUpAd + +#TODO: we don't have rainbow joints or blunts (yet?). Uncomment when (if?) they are added. +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFJoints + suffix: joints + components: + - type: Mail + contents: + # Smokeable 1 + - id: Joint + orGroup: Smokeable1 + prob: 0.35 +# - id: JointRainbow +# orGroup: Smokeable1 +# prob: 0.15 +# - id: Blunt +# orGroup: Smokeable1 +# prob: 0.35 +# - id: BluntRainbow +# orGroup: Smokeable1 +# prob: 0.15 + # Smokeable 2 + - id: Joint + orGroup: Smokeable2 + prob: 0.35 +# - id: JointRainbow +# orGroup: Smokeable2 +# prob: 0.15 +# - id: Blunt +# orGroup: Smokeable2 +# prob: 0.35 +# - id: BluntRainbow +# orGroup: Smokeable2 +# prob: 0.15 + # Smokeable 3 + - id: Joint + orGroup: Smokeable3 + prob: 0.35 +# - id: JointRainbow +# orGroup: Smokeable3 +# prob: 0.15 +# - id: Blunt +# orGroup: Smokeable3 +# prob: 0.35 +# - id: BluntRainbow +# orGroup: Smokeable3 +# prob: 0.15 + +# Catchalls for food that only exist in random spawners +# Mmm, mail food +# Needs a buff? +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFUnusualFood + suffix: unusual food + components: + - type: Mail + isPriority: true + isFragile: true + contents: + - id: FoodMealNachos + orGroup: Food + - id: FoodMealNachosCheesy + orGroup: Food + - id: FoodMealNachosCuban + orGroup: Food + - id: FoodMealEggplantParm + orGroup: Food + - id: FoodMealPotatoYaki + orGroup: Food + - id: FoodMealCornedbeef + orGroup: Food + - id: FoodMealBearsteak + orGroup: Food + - id: FoodMealPigblanket + orGroup: Food + - id: FoodMealEggsbenedict + orGroup: Food + - id: FoodMealOmelette + orGroup: Food + - id: FoodMealFriedegg + orGroup: Food + - id: FoodMealMilkape + orGroup: Food + - id: FoodMealMemoryleek + orGroup: Food + - id: DisgustingSweptSoup + orGroup: Food + - id: FoodBreadVolcanic + orGroup: Food + - id: FoodBakedWaffleSoylent + orGroup: Food + - id: FoodBakedWaffleRoffle + orGroup: Food + - id: FoodPieCherry + orGroup: Food + - id: FoodPieFrosty + orGroup: Food + prob: 0.05 + - id: FoodMeatGoliathCooked + amount: 3 + orGroup: Food + - id: FoodMeatRounyCooked + amount: 3 + orGroup: Food + - id: FoodMeatLizardCooked + amount: 3 + orGroup: Food + - id: FoodMeatSpiderlegCooked + amount: 3 + orGroup: Food + - id: FoodMeatMeatballCooked + amount: 4 + orGroup: Food + +# Needs a buff? +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFBakedGoods + suffix: baked goods + components: + - type: Mail + isPriority: true + isFragile: true + contents: + - id: FoodBakedBunHoney + amount: 2 + orGroup: Food + - id: FoodBakedBunHotX + amount: 2 + orGroup: Food + - id: FoodBakedBunMeat + amount: 2 + orGroup: Food + - id: FoodBakedPretzel + amount: 2 + orGroup: Food + - id: FoodBakedCannoli + amount: 2 + orGroup: Food + - id: FoodDonutPlain + amount: 2 + orGroup: Food + - id: FoodDonutJellyPlain + amount: 2 + orGroup: Food + - id: FoodDonutHomer + amount: 2 + orGroup: Food + - id: FoodDonutChaos + amount: 2 + orGroup: Food + - id: FoodDonutMeat + amount: 2 + orGroup: Food + - id: FoodDonutPink + amount: 2 + orGroup: Food + - id: FoodDonutSpaceman + amount: 2 + orGroup: Food + - id: FoodDonutApple + amount: 2 + orGroup: Food + - id: FoodDonutCaramel + amount: 2 + orGroup: Food + - id: FoodDonutChocolate + amount: 2 + orGroup: Food +# - id: FoodDonutBluePumpkin # Don't have that yet +# amount: 2 +# orGroup: Food + - id: FoodDonutBungo + amount: 2 + orGroup: Food + - id: FoodDonut + amount: 2 + orGroup: Food + - id: FoodDonutSweetpea + amount: 2 + orGroup: Food + - id: FoodDonutJellyHomer + amount: 2 + orGroup: Food + - id: FoodDonutJellyPink + amount: 2 + orGroup: Food + - id: FoodDonutJellySpaceman + amount: 2 + orGroup: Food + - id: FoodDonutJellyApple + amount: 2 + orGroup: Food + - id: FoodDonutJellyCaramel + amount: 2 + orGroup: Food + - id: FoodDonutJellyChocolate + amount: 2 + orGroup: Food +# - id: FoodDonutJellyBluePumpkin # Don't have that yet +# amount: 2 +# orGroup: Food + - id: FoodDonutJellyBungo + amount: 2 + orGroup: Food + - id: FoodDonutJelly + amount: 2 + orGroup: Food + - id: FoodDonutJellySweetpea + amount: 2 + orGroup: Food + - id: FoodDonutJellySlugcat + amount: 2 + orGroup: Food + - id: FoodFrozenSandwich # ah yes, baked goods + amount: 2 + orGroup: Food + - id: FoodFrozenFreezy + amount: 2 + orGroup: Food + - id: FoodFrozenSundae + amount: 2 + orGroup: Food + - id: FoodFrozenCornuto + amount: 2 + orGroup: Food + - id: FoodFrozenPopsicleOrange + amount: 2 + orGroup: Food + - id: FoodFrozenPopsicleBerry + amount: 2 + orGroup: Food + - id: FoodFrozenPopsicleJumbo + amount: 2 + orGroup: Food + - id: FoodFrozenSnowcone + amount: 2 + orGroup: Food + - id: FoodFrozenSnowconeBerry + amount: 2 + orGroup: Food + - id: FoodFrozenSnowconeFruit + amount: 2 + orGroup: Food + - id: FoodFrozenSnowconeClown + amount: 2 + orGroup: Food + - id: FoodFrozenSnowconeMime + amount: 2 + orGroup: Food + - id: FoodFrozenSnowconeRainbow + amount: 2 + orGroup: Food + - id: FoodFrozenSnowconeMime + amount: 2 + orGroup: Food + - id: FoodMealMint # unlucky + amount: 2 + orGroup: Food + +# Needs a buff? +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFUnusualProduce + suffix: unusual produce + components: + - type: Mail + isPriority: true + isFragile: true + contents: + - id: FoodLaughinPeaPod + orGroup: Produce + amount: 5 + - id: FoodMimana + orGroup: Produce + amount: 5 + - id: FoodLemoon + orGroup: Produce + amount: 5 + - id: FoodBlueTomato + orGroup: Produce + amount: 5 + - id: FoodBloodTomato + orGroup: Produce + amount: 5 + - id: FoodKoibean + orGroup: Produce + amount: 5 +# EE does not have those yet. Uncomment if any of those get ported. +# - id: FoodGhostPepper #DeltaV +# orGroup: Produce +# amount: 5 +# - id: FoodCosmicRevenant #DeltaV +# orGroup: Produce +# amount: 5 +# - id: FoodCrystalThistle #DeltaV +# orGroup: Produce +# amount: 5 + - id: FoodLily + orGroup: Produce + amount: 5 + prob: 0.5 + - id: FoodAmbrosiaDeus + orGroup: Produce + amount: 5 + prob: 0.5 + - id: FoodSpacemansTrumpet + orGroup: Produce + amount: 5 + prob: 0.25 + +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFSoaps + suffix: soap sampler + components: + - type: Mail + contents: + - id: BoxSoapsAssorted + - id: PaperMailNTSoapAd1 + orGroup: Ad + - id: PaperMailNTSoapAd2 + orGroup: Ad + +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFSoapsOmega + suffix: soap sampler, omega + components: + - type: Mail + contents: + - id: BoxSoapsAssortedOmega + - id: PaperMailNTSoapAd1 + orGroup: Ad + - id: PaperMailNTSoapAd2 + orGroup: Ad + +# Could add spessman battle rules here +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFFigurineBulk # DeltaV - No longer Bulk + suffix: figurine, bulk #DeltaV - Spams 3 boxes instead of using the bulk figurine prototype that Frontier uses + components: + - type: Mail + contents: + - id: MysteryFigureBox + - id: MysteryFigureBox + - id: MysteryFigureBox + +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFPen + suffix: fancy pen + components: + - type: Mail + contents: + - id: LuxuryPen + orGroup: Pen + prob: 0.50 + # DeltaV: Commenting these two out because I dunno if crew should have nice things + # - id: PenHop + # orGroup: Pen + # prob: 0.25 + # - id: PenCap + # orGroup: Pen + # prob: 0.25 + # TODO: come up with a slightly less powerful version of these + # Ultra-rare + # - id: CyberPen + # orGroup: Pen + # prob: 0.005 + # - id: PenCentcom + # orGroup: Pen + # prob: 0.005 + - id: PaperMailNFPaperPusherAd + +- type: entity + noSpawn: true + parent: BaseMailLarge + id: MailNFThrongler + suffix: throngler + components: + - type: Mail + contents: + - id: ThronglerToy + +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFInstrumentSmall + suffix: instrument, expanded + components: + - type: Mail + contents: + - id: TrumpetInstrument + orGroup: Instrument + - id: RecorderInstrument + orGroup: Instrument + - id: ClarinetInstrument + orGroup: Instrument + - id: FluteInstrument + orGroup: Instrument + - id: HarmonicaInstrument + orGroup: Instrument + - id: OcarinaInstrument + orGroup: Instrument + - id: PanFluteInstrument + orGroup: Instrument + - id: KalimbaInstrument + orGroup: Instrument + - id: WoodblockInstrument + orGroup: Instrument + - id: BikeHornInstrument + orGroup: Instrument + - id: MusicBoxInstrument + orGroup: Instrument + - id: MicrophoneInstrument + orGroup: Instrument + - id: MusicalLungInstrument + orGroup: Instrument + # Uncommon + - id: PhoneInstrument + orGroup: Instrument + prob: 0.1 + # Rare + - id: BananaPhoneInstrument + orGroup: Instrument + prob: 0.05 + # Ultra-rare + - id: PhoneInstrumentSyndicate + orGroup: Instrument + prob: 0.01 + +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFUnusualClothing + suffix: unusual clothing + components: + - type: Mail + contents: + - id: ClothingKimonoPink + orGroup: Clothes + - id: ClothingKimonoBlue + orGroup: Clothes + - id: ClothingKimonoPurple + orGroup: Clothes + - id: ClothingKimonoSky + orGroup: Clothes + - id: ClothingKimonoGreen + orGroup: Clothes + - id: ClothingUniformMartialGi + orGroup: Clothes + - id: ClothingNeckBling + orGroup: Clothes + - id: ClothingShoesBling + orGroup: Clothes + - id: ClothingNeckCloakAdmin + orGroup: Clothes + - id: ClothingHeadHatFancyCrown + orGroup: Clothes + - id: ClothingHeadHatCake + orGroup: Clothes + - id: ClothingHeadHatCone + orGroup: Clothes + - id: ClothingMaskOniRed + orGroup: Clothes + - id: ClothingMaskOniBlue + orGroup: Clothes + - id: ClothingHeadHatRichard + orGroup: Clothes + - id: ClothingHeadHatAnimalHeadslime + orGroup: Clothes + - id: ClothingHeadHatDogEars + orGroup: Clothes + - id: ClothingHeadHatCatEars + orGroup: Clothes + - id: ClothingEyesGlassesOutlawGlasses + orGroup: Clothes + - id: ClothingUniformJumpsuitGalaxyBlue + orGroup: Clothes + prob: 0.25 + - id: ClothingUniformJumpsuitGalaxyRed + orGroup: Clothes + prob: 0.25 + +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFCritter + suffix: critter + components: + - type: Mail + isFragile: true + contents: + # Bugs (weight: 2) + - id: MobCockroach + orGroup: Critter + prob: 0.36 + - id: MobSlug + orGroup: Critter + prob: 0.36 + - id: MobArgocyteSlurva # honorary bug? + orGroup: Critter + prob: 0.36 + - id: MobBee + orGroup: Critter + prob: 0.36 + - id: MobButterfly + orGroup: Critter + prob: 0.36 + # Uncommon + - id: MobMothroach + orGroup: Critter + prob: 0.2 + # Small reptiles (weight: 1) + - id: MobLizard + orGroup: Critter + prob: 0.34 + - id: MobSnake + orGroup: Critter + prob: 0.33 + - id: MobFrog + orGroup: Critter + prob: 0.33 + # Small mammals (weight: 1) + - id: MobMouse + orGroup: Critter + prob: 0.33 + - id: MobMouse1 + orGroup: Critter + prob: 0.33 + - id: MobMouse2 + orGroup: Critter + prob: 0.33 + - id: MobMouseCancer + orGroup: Critter + prob: 0.01 # Rare + +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFTacticalMaid + suffix: tactical maid + components: + - type: Mail + contents: + - id: ClothingUniformJumpskirtTacticalMaid + - id: ClothingHeadHatTacticalMaidHeadband + - id: MegaSprayBottle + - id: ClothingHandsTacticalMaidGloves + +- type: entity + noSpawn: true + parent: BaseMailLarge + id: MailNFKendoKit + suffix: kendo kit + components: + - type: Mail + contents: # A lot of stuff here, seems spammy. + - id: ClothingUniformKendoHakama + amount: 2 + - id: ClothingOuterArmorKendoBogu + amount: 2 + - id: ClothingHeadHelmetKendoMen + amount: 2 + - id: Shinai + amount: 2 + +# Base Nyano Mail +- type: entity + noSpawn: true + parent: BaseMail + id: MailSake + suffix: osake + components: + - type: Mail + contents: + - id: DrinkSakeCup + amount: 2 + - id: DrinkTokkuri + +- type: entity + noSpawn: true + parent: BaseMail + id: MailBlockGameDIY + suffix: blockgamediy + components: + - type: Mail + contents: + - id: BlockGameArcadeComputerCircuitboard + +- type: entity + noSpawn: true + parent: BaseMail + id: MailCigars + suffix: Cigars + components: + - type: Mail + contents: + - id: CigarCase + - id: Lighter + +- type: entity + noSpawn: true + parent: BaseMail + id: MailCrayon + suffix: Crayon + components: + - type: Mail + contents: + - id: CrayonBox + +- type: entity + noSpawn: true + parent: BaseMail + id: MailCosplayArc + suffix: cosplay-arc + components: + - type: Mail + openSound: /Audio/Nyanotrasen/Voice/Felinid/cat_wilhelm.ogg + contents: + - id: ClothingCostumeArcDress + +- type: entity + noSpawn: true + parent: BaseMail + id: MailCosplayGeisha + suffix: cosplay-geisha + components: + - type: Mail + contents: + - id: UniformGeisha + - id: DrinkTeapot + - id: DrinkTeacup + amount: 3 + +- type: entity + noSpawn: true + parent: BaseMail + id: MailCosplaySchoolgirl + suffix: cosplay-schoolgirl + components: + - type: Mail + contents: + - id: UniformSchoolgirlBlack + orGroup: Color + - id: UniformSchoolgirlBlue + orGroup: Color + - id: UniformSchoolgirlCyan + orGroup: Color + - id: UniformSchoolgirlGreen + orGroup: Color + - id: UniformSchoolgirlOrange + orGroup: Color + - id: UniformSchoolgirlPink + orGroup: Color + - id: UniformSchoolgirlPurple + orGroup: Color + - id: UniformSchoolgirlRed + orGroup: Color + +- type: entity + noSpawn: true + parent: BaseMail + id: MailFlowers + suffix: flowers + components: + - type: Mail + contents: + # TODO actual flowers + - id: ClothingHeadHatFlowerWreath + orGroup: Flowers + - id: FoodPoppy + orGroup: Flowers + - id: FoodLily + +- type: entity + noSpawn: true + parent: BaseMail + id: MailSignallerKit + suffix: signallerkit + components: + - type: Mail + contents: + - id: Multitool + - id: RemoteSignaller + +- type: entity + noSpawn: true + parent: BaseMail + id: MailNoir + suffix: noir + components: + - type: Mail + contents: + - id: ClothingUniformJumpsuitDetectiveGrey + - id: ClothingUniformJumpskirtDetectiveGrey + - id: ClothingHeadHatBowlerHat + - id: ClothingOuterCoatGentle + +- type: entity + noSpawn: true + parent: BaseMail + id: MailRestraints + suffix: restraints + components: + - type: Mail + contents: + - id: Handcuffs + - id: ClothingMaskMuzzle + - id: ClothingEyesBlindfold + +- type: entity + noSpawn: true + parent: BaseMail + id: MailFishingCap + suffix: fishingcap + components: + - type: Mail + contents: + - id: ClothingHeadFishCap + +- type: entity + noSpawn: true + parent: BaseMail + id: MailFlashlight + suffix: Flashlight + components: + - type: Mail + contents: + - id: FlashlightLantern + +- type: entity + noSpawn: true + parent: BaseMail + id: MailSpaceVillainDIY + suffix: spacevilliandiy + components: + - type: Mail + contents: + - id: SpaceVillainArcadeComputerCircuitboard + +- type: entity + noSpawn: true + parent: BaseMail + id: MailSunglasses + suffix: Sunglasses + components: + - type: Mail + contents: + - id: ClothingEyesGlassesSunglasses + +- type: entity + noSpawn: true + parent: BaseMail + id: MailWinterCoat + suffix: wintercoat + components: + - type: Mail + contents: + - id: ClothingOuterWinterCoat + orGroup: Coat + - id: ClothingOuterWinterCoatLong + orGroup: Coat + - id: ClothingOuterWinterCoatPlaid + orGroup: Coat diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Mail/mail_civilian.yml b/Resources/Prototypes/DeltaV/Entities/Objects/Specific/Mail/mail_civilian.yml similarity index 71% rename from Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Mail/mail_civilian.yml rename to Resources/Prototypes/DeltaV/Entities/Objects/Specific/Mail/mail_civilian.yml index a41fac14ff..19a1ee3c53 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Mail/mail_civilian.yml +++ b/Resources/Prototypes/DeltaV/Entities/Objects/Specific/Mail/mail_civilian.yml @@ -1,8 +1,62 @@ +# Frontier Mail +- type: entity + noSpawn: true + parent: BaseMailLarge + id: MailNFInstrumentLarge + suffix: instrument, large + components: + - type: Mail + contents: + - id: TromboneInstrument + orGroup: Instrument + - id: FrenchHornInstrument + orGroup: Instrument + - id: SaxophoneInstrument + orGroup: Instrument + - id: EuphoniumInstrument + orGroup: Instrument + - id: AcousticGuitarInstrument + orGroup: Instrument + - id: ElectricGuitarInstrument + orGroup: Instrument + - id: BassGuitarInstrument + orGroup: Instrument + - id: RockGuitarInstrument + orGroup: Instrument + - id: BanjoInstrument + orGroup: Instrument + - id: ViolinInstrument + orGroup: Instrument + - id: CelloInstrument + orGroup: Instrument + - id: ViolaInstrument + orGroup: Instrument + - id: BagpipeInstrument # Fury. + orGroup: Instrument + - id: SynthesizerInstrument + orGroup: Instrument + - id: AccordionInstrument + orGroup: Instrument + - id: GlockenspielInstrument + orGroup: Instrument + - id: XylophoneInstrument + orGroup: Instrument + # Uncommon + - id: Rickenbacker4003Instrument + orGroup: Instrument + prob: 0.25 + # Rare + - id: Rickenbacker4001Instrument + orGroup: Instrument + prob: 0.1 + +# Base Nyano Mail + - type: entity noSpawn: true parent: BaseMail id: MailBotanistChemicalBottles - suffix: botanistchemicals + suffix: botanist chemicals components: - type: Mail contents: @@ -102,7 +156,7 @@ noSpawn: true parent: BaseMail id: MailHoPBureaucracy - suffix: hoppaper + suffix: hop paper components: - type: Mail contents: @@ -113,7 +167,7 @@ noSpawn: true parent: BaseMail id: MailHoPSupplement - suffix: hopsupplement + suffix: hop supplement components: - type: Mail contents: @@ -125,7 +179,7 @@ noSpawn: true parent: BaseMail id: MailMimeArtsCrafts - suffix: artscrafts + suffix: arts and crafts components: - type: Mail contents: @@ -137,7 +191,7 @@ noSpawn: true parent: BaseMail id: MailMimeBlankBook - suffix: blankbook + suffix: blank book components: - type: Mail contents: @@ -147,37 +201,17 @@ noSpawn: true parent: BaseMail id: MailMimeBottleOfNothing - suffix: bottleofnothing + suffix: bottle of nothing components: - type: Mail contents: - id: DrinkBottleOfNothingFull -- type: entity - noSpawn: true - parent: BaseMail - id: MailMusicianInstrumentSmall - suffix: instrument-small - components: - - type: Mail - isFragile: true - contents: - - id: FluteInstrument - orGroup: Instrument - - id: HarmonicaInstrument - orGroup: Instrument - - id: OcarinaInstrument - orGroup: Instrument - - id: PanFluteInstrument - orGroup: Instrument - - id: RecorderInstrument - orGroup: Instrument - - type: entity noSpawn: true parent: BaseMail id: MailPassengerMoney - suffix: passengermoney + suffix: passenger money components: - type: Mail contents: diff --git a/Resources/Prototypes/DeltaV/Entities/Objects/Specific/Mail/mail_command.yml b/Resources/Prototypes/DeltaV/Entities/Objects/Specific/Mail/mail_command.yml new file mode 100644 index 0000000000..86dec46a65 --- /dev/null +++ b/Resources/Prototypes/DeltaV/Entities/Objects/Specific/Mail/mail_command.yml @@ -0,0 +1,35 @@ +# Base Nyano Mail +- type: entity + noSpawn: true + parent: BaseMail + id: MailCommandPinpointerNuclear + suffix: pinpointer mail ops + components: + - type: Mail + contents: + - id: PinpointerNuclear + +- type: entity + noSpawn: true + parent: BaseMail + id: MailStationRepNFNukeDisk + suffix: nuke disk + components: + - type: Mail + isFragile: true + contents: + - id: NukeDiskFake + - id: PaperMailNFAntivirus + +- type: entity + noSpawn: true + parent: BaseMail + id: MailCommandNFPipebombIntern + suffix: pipe and bomb + components: + - type: Mail + isFragile: true + contents: + - id: SmokingPipeFilledTobacco + - id: DrinkAtomicBombGlass + - id: PaperMailNFPipebombIntern diff --git a/Resources/Prototypes/DeltaV/Entities/Objects/Specific/Mail/mail_engineering.yml b/Resources/Prototypes/DeltaV/Entities/Objects/Specific/Mail/mail_engineering.yml new file mode 100644 index 0000000000..af70fc621c --- /dev/null +++ b/Resources/Prototypes/DeltaV/Entities/Objects/Specific/Mail/mail_engineering.yml @@ -0,0 +1,182 @@ +# Base Nyano Mail +- type: entity + noSpawn: true + parent: BaseMail + id: MailEngineeringCables + suffix: cables + components: + - type: Mail + contents: + - id: CableHVStack + orGroup: Cables + - id: CableMVStack + orGroup: Cables + - id: CableApcStack + orGroup: Cables + +- type: entity + noSpawn: true + parent: BaseMail + id: MailEngineeringKudzuDeterrent + suffix: antikudzu + components: + - type: Mail + contents: + - id: PlantBGoneSpray + +- type: entity + noSpawn: true + parent: BaseMail + id: MailEngineeringSheetGlass + suffix: sheetglass + components: + - type: Mail + contents: + - id: SheetGlass + +- type: entity + noSpawn: true + parent: BaseMail + id: MailEngineeringWelderReplacement + suffix: welder + components: + - type: Mail + contents: + - id: Welder + +# Frontier Mail + +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFCircuitboardIndustrial + suffix: industrial circuitboard + components: + - type: Mail + contents: + - id: ThermomachineFreezerMachineCircuitBoard + orGroup: Board + prob: 0.5 + - id: ThermomachineHeaterMachineCircuitBoard + orGroup: Board + prob: 0.5 + - id: HellfireFreezerMachineCircuitBoard + orGroup: Board + prob: 0.25 + - id: HellfireHeaterMachineCircuitBoard + orGroup: Board + prob: 0.25 + - id: CryoPodMachineCircuitboard # Medical as well + orGroup: Board + prob: 0.5 + - id: ChemMasterMachineCircuitboard + orGroup: Board + prob: 0.5 + - id: ChemDispenserMachineCircuitboard + orGroup: Board + prob: 0.25 + - id: StasisBedMachineCircuitboard + orGroup: Board + prob: 0.25 + - id: BiomassReclaimerMachineCircuitboard + orGroup: Board + prob: 0.25 + - id: BiofabricatorMachineCircuitboard + orGroup: Board + prob: 0.25 + - id: TurboItemRechargerCircuitboard + orGroup: Board + prob: 0.5 + - id: AutolatheHyperConvectionMachineCircuitboard + orGroup: Board + prob: 0.25 + - id: ProtolatheHyperConvectionMachineCircuitboard + orGroup: Board + prob: 0.25 + - id: HotplateMachineCircuitboard + orGroup: Board + prob: 0.5 +# - id: CircuitImprinterHyperConvectionMachineCircuitboard +# orGroup: Board +# prob: 0.25 + - id: SheetifierMachineCircuitboard + orGroup: Board + prob: 0.25 + - id: RadarConsoleCircuitboard + orGroup: Board + prob: 0.25 + - id: OreProcessorIndustrialMachineCircuitboard + orGroup: Board + prob: 0.5 + - id: GasRecyclerMachineCircuitboard + orGroup: Board + prob: 0.1 + +- type: entity + noSpawn: true + parent: BaseMail + id: MailNFCircuitboardService + suffix: service circuitboard + components: + - type: Mail + contents: + - id: ComputerTelevisionCircuitboard + orGroup: Board + - id: ReagentGrinderMachineCircuitboard + orGroup: Board + - id: ReagentGrinderIndustrialMachineCircuitboard + orGroup: Board + prob: 0.5 + - id: SurveillanceWirelessCameraMovableCircuitboard + orGroup: Board + prob: 0.5 + - id: MicrowaveMachineCircuitboard + orGroup: Board + - id: ElectricGrillMachineCircuitboard + orGroup: Board + prob: 0.5 + - id: FatExtractorMachineCircuitboard + orGroup: Board + prob: 0.25 + - id: SeedExtractorMachineCircuitboard + orGroup: Board + prob: 0.5 + - id: BoozeDispenserMachineCircuitboard + orGroup: Board + - id: SodaDispenserMachineCircuitboard + orGroup: Board + - id: JukeboxCircuitBoard + orGroup: Board + - id: TelecomServerCircuitboard + orGroup: Board + prob: 0.25 + - id: ComputerMassMediaCircuitboard + orGroup: Board + prob: 0.1 + +- type: entity + noSpawn: true + parent: BaseMailLarge + id: MailNFPowerTool + suffix: power tool + components: + - type: Mail + contents: + - id: PaperMailNFPowerTool + orGroup: Paper + - id: JawsOfLife + orGroup: Gift + prob: 0.33 + - id: PowerDrill + orGroup: Gift + prob: 0.33 + - id: WelderIndustrial + orGroup: Gift + prob: 0.20 + # Rare + - id: WelderIndustrialAdvanced + orGroup: Gift + prob: 0.10 + - id: WelderExperimental + orGroup: Gift + prob: 0.04 diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Mail/mail_epistemology.yml b/Resources/Prototypes/DeltaV/Entities/Objects/Specific/Mail/mail_epistemology.yml similarity index 74% rename from Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Mail/mail_epistemology.yml rename to Resources/Prototypes/DeltaV/Entities/Objects/Specific/Mail/mail_epistemology.yml index 38526966b8..be6f9818e2 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Mail/mail_epistemology.yml +++ b/Resources/Prototypes/DeltaV/Entities/Objects/Specific/Mail/mail_epistemology.yml @@ -46,13 +46,3 @@ - type: Mail contents: - id: ClothingHeadTinfoil - -- type: entity - noSpawn: true - parent: BaseMail - id: MailDetectiveForensicSupplement # Deltav - Detective is in charge of investigating crimes. - suffix: detectivesupplement # Deltav - Detective is in charge of investigating crimes. - components: - - type: Mail - contents: - - id: BoxForensicPad diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Mail/mail_medical.yml b/Resources/Prototypes/DeltaV/Entities/Objects/Specific/Mail/mail_medical.yml similarity index 84% rename from Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Mail/mail_medical.yml rename to Resources/Prototypes/DeltaV/Entities/Objects/Specific/Mail/mail_medical.yml index 4e797272e5..735f840a20 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Mail/mail_medical.yml +++ b/Resources/Prototypes/DeltaV/Entities/Objects/Specific/Mail/mail_medical.yml @@ -1,3 +1,4 @@ +# Base Nyano Mail - type: entity noSpawn: true parent: BaseMail @@ -91,3 +92,19 @@ maxAmount: 2 - id: SyringeTranexamicAcid maxAmount: 2 + +# Frontier Mail +- type: entity + parent: BaseMailLarge + id: MailNFMedkit + suffix: medkit + components: + - type: Mail + contents: + - id: MedkitAdvancedFilled + orGroup: Medkit + prob: 0.75 + - id: MedkitCombatFilled + orGroup: Medkit + prob: 0.25 + diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Mail/mail_security.yml b/Resources/Prototypes/DeltaV/Entities/Objects/Specific/Mail/mail_security.yml similarity index 50% rename from Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Mail/mail_security.yml rename to Resources/Prototypes/DeltaV/Entities/Objects/Specific/Mail/mail_security.yml index b47d5af56e..eed846a090 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Mail/mail_security.yml +++ b/Resources/Prototypes/DeltaV/Entities/Objects/Specific/Mail/mail_security.yml @@ -1,3 +1,4 @@ +# Base Nyano Mail - type: entity noSpawn: true parent: BaseMail @@ -34,7 +35,6 @@ maxAmount: 2 #- type: entity -# noSpawn: true # parent: BaseMail # id: MailSecuritySpaceLaw # suffix: spacelaw @@ -54,3 +54,42 @@ contents: - id: BoxBeanbag +- type: entity + noSpawn: true + parent: BaseMail + id: MailDetectiveForensicSupplement # Deltav - Detective is in charge of investigating crimes. + suffix: detectivesupplement # Deltav - Detective is in charge of investigating crimes. + components: + - type: Mail + contents: + - id: BoxForensicPad + +# Frontier Mail + +- type: entity + noSpawn: true + parent: BaseMailLarge + id: MailSecurityNFMusket + suffix: musket + components: + - type: Mail + contents: + - id: ClothingHeadHatPwig + - id: Musket + - id: CartridgeAntiMateriel + amount: 2 + - id: PaperMailNTMusket + +# Delta Mail + +- type: entity + noSpawn: true + parent: BaseMail + id: MailSecurityDVSpaceLaw + suffix: spacelaw, extended + components: + - type: Mail + contents: + - id: BookSecurity +# - id: BookSOP #This is where I'd put my Delta SOP book... IF I HAD ONE!!! + - id: PaperMailNFSpaceLaw # Uses the NF space law paper, which is edited to mention the Delta Sector diff --git a/Resources/Prototypes/DeltaV/Mail/mailDeliveries.yml b/Resources/Prototypes/DeltaV/Mail/mailDeliveries.yml new file mode 100644 index 0000000000..c0d6df9ec2 --- /dev/null +++ b/Resources/Prototypes/DeltaV/Mail/mailDeliveries.yml @@ -0,0 +1,147 @@ +- type: mailDeliveryPool + id: RandomDeltaVMailDeliveryPool #This entire table is messy as fuck but the weights add up to 35. TODO: ORGANIZE THIS SHIT AAAA + everyone: + MailNFAlcohol: 1 + MailNFBakedGoods: 1 + MailNFBible: 1 + MailNFBikeHorn: 0.5 + MailBooksAll: 1 + MailBlockGameDIY: 0.5 + MailNFBuildABuddy: 0.3 + MailDVBoxes: 0.3 + MailCrayon: 1 + MailNFCake: 1 + MailNFCheese: 1 + MailNFCookies: 1.1 + MailNFCritter: 1 + #Cigarettes - Adds up to 1 in weight + MailNFCigarettes: 0.5 + MailCigars: 0.2 + MailNFJoints: 0.2 + MailNFGoldCigars: 0.1 + #Cosplay - Adds up to 3.4 in weight + MailCosplayArc: 0.5 + MailDVCosplayFakeWizard: 0.5 + MailNFCosplayWizard: 0.5 + MailNFCosplayMaid: 0.5 + MailCosplayGeisha: 0.5 + MailCosplaySchoolgirl: 0.5 + MailNFCosplayNurse: 0.4 + MailNFDonkPockets: 0.5 + MailNFEMP: 0.3 + MailNFFigurineBulk: 1 + MailFishingCap: 0.5 + MailFlashlight: 1 + MailFlowers: 1 + MailNFKendoKit: 0.3 + MailNFKnife: 0.7 + MailNFMuffins: 1 + MailNoir: 0.5 + MailNFPAI: 1.2 + MailNFPlushie: 1 + MailPumpkinPie: 0.3 + MailNFPen: 0.7 + MailRestraints: 0.8 + MailSake: 0.4 + MailDVScarves: 0.15 + MailNFSnacks: 1 + #Soda - Adds up to 1 in weight + MailNFSodaPwrGame: 0.2 + MailNFSodaRedBool: 0.2 + MailNFSodaSpaceCola: 0.2 + MailNFSodaSpaceMountainWind: 0.2 + MailNFSodaSpaceUp: 0.2 + #End Soda + MailNFSmoke: 0.4 + MailSpaceVillainDIY: 0.5 + MailSignallerKit: 0.5 + MailSunglasses: 1 + MailNFSoaps: 0.5 + MailNFSoapsOmega: 0.5 + MailNFSword: 0.5 + MailNFTacticalMaid: 0.5 + MailNFThrongler: 0.05 + MailNFUnusualClothing: 0.5 + MailNFUnusualFood: 1 + MailNFUnusualProduce: 1 + MailNFVagueThreat: 0.5 + # Mainly for Glacier + MailWinterCoat: 1.5 + + # Department and job-specific mail can have slightly higher weights, + # since they'll be merged with the everyone pool. + departments: + Medical: + MailMedicalBasicSupplies: 2 + MailMedicalChemistrySupplement: 2 + MailMedicalEmergencyPens: 3 + MailMedicalMedicinePills: 2 + MailMedicalSheetPlasma: 1 + # MailMedicalSpaceacillin: 1 + MailMedicalStabilizers: 2 + MailNFMedkit: 2 + Engineering: + MailAMEGuide: 1 + MailEngineeringCables: 2 + MailEngineeringKudzuDeterrent: 2 + MailEngineeringSheetGlass: 2 + MailEngineeringWelderReplacement: 2 + MailNFCircuitboardIndustrial: 2 + MailNFCircuitboardService: 1 + MailNFPowerTool: 1 + Security: + MailSecurityDonuts: 3 + MailSecurityFlashlight: 2 + MailSecurityNonlethalsKit: 2 + MailSecurityDVSpaceLaw: 1 + MailSecurityNFMusket: 1 + Epistemics: +# MailBooks: 1 + MailEpistemologyBluespace: 1 + MailEpistemologyIngotGold: 2 + MailEpistemologyResearchDisk: 1 + MailEpistemologyTinfoilHat: 1 + MailSignallerKit: 1 + # All heads of staff are in Command and not their departments, technically. + # So any items from the departments above that should also be sent to the + # respective department heads should be duplicated below. + Command: + MailCommandPinpointerNuclear: 0.5 + MailStationRepNFNukeDisk: 0.3 + MailCommandNFPipebombIntern: 0.1 + + jobs: + Botanist: + MailBotanistChemicalBottles: 2 + MailBotanistMutagen: 1.5 + MailBotanistSeeds: 1 + ChiefEngineer: + MailEngineeringKudzuDeterrent: 2 + ChiefMedicalOfficer: + MailMedicalEmergencyPens: 2 + MailMedicalMedicinePills: 3 + MailMedicalSheetPlasma: 2 + Clown: + MailClownGildedBikeHorn: 0.5 + MailClownHonkSupplement: 3 + Detective: # Deltav - Detective is in charge of investigating crimes. + MailDetectiveForensicSupplement: 2 # Deltav - Detective is in charge of investigating crimes. + HeadOfPersonnel: + MailHoPBureaucracy: 2 + MailHoPSupplement: 3 + HeadOfSecurity: + MailSecurityNonlethalsKit: 2 + Lawyer: + MailSecurityDVSpaceLaw: 2 + Mime: + MailMimeArtsCrafts: 3 + MailMimeBlankBook: 2 + MailMimeBottleOfNothing: 1 + ResearchDirector: # DeltaV - Epistemics Department replacing Science but keeping their IDs + MailEpistemologyIngotGold: 2 + Musician: + MailMusicianInstrumentSmall: 1 + Passenger: + MailPassengerMoney: 3 + Warden: + MailWardenCrowdControl: 2 diff --git a/Resources/Prototypes/Entities/Clothing/Uniforms/jumpsuits.yml b/Resources/Prototypes/Entities/Clothing/Uniforms/jumpsuits.yml index 0c9f06a58b..2d2961c424 100644 --- a/Resources/Prototypes/Entities/Clothing/Uniforms/jumpsuits.yml +++ b/Resources/Prototypes/Entities/Clothing/Uniforms/jumpsuits.yml @@ -854,72 +854,69 @@ id: ClothingUniformJumpsuitLibrarianOrionFlipped name: orion express librarian jumpsuit -################################ -# Unused Librarian Jumpsuits # -################################ -# - type: entity -# parent: ClothingUniformBaseFlippable -# id: ClothingUniformJumpsuitLibrarianHeph -# name: hephaestus industries librarian jumpsuit -# description: A cosy green jumper fit for a curator of books. -# components: -# - type: Sprite -# sprite: Clothing/Uniforms/Jumpsuit/librarian_heph.rsi -# - type: Clothing -# sprite: Clothing/Uniforms/Jumpsuit/librarian_heph.rsi - -# - type: entity -# parent: [ ClothingUniformBaseFlipped, ClothingUniformJumpsuitLibrarianHeph ] -# id: ClothingUniformJumpsuitLibrarianHephFlipped -# name: hephaestus industries librarian jumpsuit - -# - type: entity -# parent: ClothingUniformBaseFlippable -# id: ClothingUniformJumpsuitLibrarianPMCG -# name: private military contracting group librarian jumpsuit -# description: A cosy white jumper fit for a curator of books. -# components: -# - type: Sprite -# sprite: Clothing/Uniforms/Jumpsuit/librarian_pmcg.rsi -# - type: Clothing -# sprite: Clothing/Uniforms/Jumpsuit/librarian_pmcg.rsi - -# - type: entity -# parent: [ ClothingUniformBaseFlipped, ClothingUniformJumpsuitLibrarianPMCG ] -# id: ClothingUniformJumpsuitLibrarianPMCGFlipped -# name: private military contracting group librarian jumpsuit - -# - type: entity -# parent: ClothingUniformBaseFlippable -# id: ClothingUniformJumpsuitLibrarianZav -# name: zavodskoi interstellar librarian jumpsuit -# description: A blood brown jumper fit for a curator of books. -# components: -# - type: Sprite -# sprite: Clothing/Uniforms/Jumpsuit/librarian_zav.rsi -# - type: Clothing -# sprite: Clothing/Uniforms/Jumpsuit/librarian_zav.rsi - -# - type: entity -# parent: [ ClothingUniformBaseFlipped, ClothingUniformJumpsuitLibrarianZav ] -# id: ClothingUniformJumpsuitLibrarianZavFlipped -# name: zavodskoi interstellar librarian jumpsuit - -# - type: entity -# parent: ClothingUniformBaseFlippable -# id: ClothingUniformJumpsuitLibrarianZeng -# name: zeng-hu pharmaceuticals librarian jumpsuit -# description: A blood brown jumper fit for a curator of books. -# components: -# - type: Sprite -# sprite: Clothing/Uniforms/Jumpsuit/librarian_zeng.rsi -# - type: Clothing -# sprite: Clothing/Uniforms/Jumpsuit/librarian_zeng.rsi - -# - type: entity -# parent: [ ClothingUniformBaseFlipped, ClothingUniformJumpsuitLibrarianZeng ] -# id: ClothingUniformJumpsuitLibrarianZengFlipped -# name: zeng-hu pharmaceuticals librarian jumpsuit +- type: entity + parent: ClothingUniformBaseFlippable + id: ClothingUniformJumpsuitLibrarianHeph + name: hephaestus industries librarian jumpsuit + description: A cosy green jumper fit for a curator of books. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpsuit/librarian_heph.rsi + - type: Clothing + sprite: Clothing/Uniforms/Jumpsuit/librarian_heph.rsi + +- type: entity + parent: [ ClothingUniformBaseFlipped, ClothingUniformJumpsuitLibrarianHeph ] + id: ClothingUniformJumpsuitLibrarianHephFlipped + name: hephaestus industries librarian jumpsuit + +- type: entity + parent: ClothingUniformBaseFlippable + id: ClothingUniformJumpsuitLibrarianPMCG + name: private military contracting group librarian jumpsuit + description: A cosy white jumper fit for a curator of books. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpsuit/librarian_pmcg.rsi + - type: Clothing + sprite: Clothing/Uniforms/Jumpsuit/librarian_pmcg.rsi + +- type: entity + parent: [ ClothingUniformBaseFlipped, ClothingUniformJumpsuitLibrarianPMCG ] + id: ClothingUniformJumpsuitLibrarianPMCGFlipped + name: private military contracting group librarian jumpsuit + +- type: entity + parent: ClothingUniformBaseFlippable + id: ClothingUniformJumpsuitLibrarianZav + name: zavodskoi interstellar librarian jumpsuit + description: A blood brown jumper fit for a curator of books. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpsuit/librarian_zav.rsi + - type: Clothing + sprite: Clothing/Uniforms/Jumpsuit/librarian_zav.rsi + +- type: entity + parent: [ ClothingUniformBaseFlipped, ClothingUniformJumpsuitLibrarianZav ] + id: ClothingUniformJumpsuitLibrarianZavFlipped + name: zavodskoi interstellar librarian jumpsuit + +- type: entity + parent: ClothingUniformBaseFlippable + id: ClothingUniformJumpsuitLibrarianZeng + name: zeng-hu pharmaceuticals librarian jumpsuit + description: A blood brown jumper fit for a curator of books. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpsuit/librarian_zeng.rsi + - type: Clothing + sprite: Clothing/Uniforms/Jumpsuit/librarian_zeng.rsi + +- type: entity + parent: [ ClothingUniformBaseFlipped, ClothingUniformJumpsuitLibrarianZeng ] + id: ClothingUniformJumpsuitLibrarianZengFlipped + name: zeng-hu pharmaceuticals librarian jumpsuit - type: entity parent: ClothingUniformBase diff --git a/Resources/Prototypes/Entities/Objects/Devices/pda.yml b/Resources/Prototypes/Entities/Objects/Devices/pda.yml index 1a38899783..ebefd05cbb 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/pda.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/pda.yml @@ -358,6 +358,12 @@ accentVColor: "#a23e3e" - type: Icon state: pda-qm + - type: CartridgeLoader # Adds the MailMetrics courier tracker + preinstalled: + - CrewManifestCartridge + - NotekeeperCartridge + - NewsReaderCartridge + - MailMetricsCartridge - type: entity parent: BasePDA diff --git a/Resources/Prototypes/Entities/Objects/Misc/mail_capsule.yml b/Resources/Prototypes/Entities/Objects/Misc/mail_capsule.yml new file mode 100644 index 0000000000..bd6c9057cd --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Misc/mail_capsule.yml @@ -0,0 +1,139 @@ +- type: entity + name: mail capsule + suffix: Primed + id: MailCapsulePrimed + parent: BaseItem + components: + - type: ThrowingAngle + angle: 180 + - type: EmbeddableProjectile + minimumSpeed: 1 + removalTime: 0.1 + - type: Tag + tags: + - MailCapsule + - Trash + - type: Sprite + sprite: Objects/Misc/mail_capsule.rsi + layers: + - state: icon-empty + - type: ItemSlots + slots: + mail_slot: + insertVerbText: Put in Mail + ejectVerbText: Take out Mail + name: Mail + startingItem: null + whitelist: + tags: + - Book + - Document + - Mail + components: + - Mail + - Paper + - HyperlinkBook + insertOnInteract: true + priority: 3 + food_slot: + insertVerbText: Put in Food + ejectVerbText: Take out Food + name: Food + startingItem: null + whitelist: + components: + - Food + insertOnInteract: true + priority: 2 + cash_slot: + insertVerbText: Put in Cash + ejectVerbText: Take out Cash + name: Cash + startingItem: null + whitelist: + components: + - Currency + insertOnInteract: true + priority: 1 + - type: ContainerContainer + containers: + storagebase: !type:Container + showEnts: False + occludes: true + ents: [] + mail_slot: !type:ContainerSlot + showEnts: False + occludes: true + ent: null + food_slot: !type:ContainerSlot + showEnts: False + occludes: true + ent: null + cash_slot: !type:ContainerSlot + showEnts: False + occludes: true + ent: null + - type: Appearance + - type: ItemMapper + mapLayers: + icon-food: + whitelist: + components: + - Food + icon-cash: + whitelist: + components: + - Currency + icon-mail: + whitelist: + tags: + - Book + - Document + - Mail + components: + - Mail + - Paper + - HyperlinkBook + sprite: Objects/Misc/mail_capsule.rsi + - type: Dumpable + - type: Damageable + damageContainer: Inorganic + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 20 #excess damage avoids cost of spawning entities. + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - trigger: + !type:DamageTrigger + damage: 10 + behaviors: + - !type:PlaySoundBehavior + sound: + collection: GlassBreak + - !type:EmptyAllContainersBehaviour + - !type:DoActsBehavior + acts: [ "Destruction" ] + - type: DamageOnLand + damage: + types: + Blunt: 9.5 + +- type: entity + name: mail capsule box + parent: BoxCardboard + id: BoxMailCapsulePrimed + description: A box of primed mail capsules. + components: + - type: Storage + grid: + - 0,0,4,3 + - type: StorageFill + contents: + - id: MailCapsulePrimed + amount: 10 + - type: Sprite + layers: + - state: box diff --git a/Resources/Prototypes/Entities/Objects/Misc/spider_web.yml b/Resources/Prototypes/Entities/Objects/Misc/spider_web.yml index bb284000a7..68ba94b578 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/spider_web.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/spider_web.yml @@ -76,6 +76,9 @@ ignoreWhitelist: components: - IgnoreSpiderWeb + - type: Tag + tags: + - ArachneWeb - type: entity id: SpiderWebClown diff --git a/Resources/Prototypes/Entities/Objects/Specific/Mail/Items/boxes.yml b/Resources/Prototypes/Entities/Objects/Specific/Mail/Items/boxes.yml new file mode 100644 index 0000000000..536736fc90 --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Specific/Mail/Items/boxes.yml @@ -0,0 +1,212 @@ +# Mail-only boxes. If/when something uses these outside of the mail, move the entry into Catalog/Fills. + +- type: entity + name: scented soap sampler pack + parent: BoxCardboard + id: BoxSoapsAssorted + description: A box of various scented soaps. Ooh, lavender. + components: + - type: StorageFill + contents: + - id: SoapNT + amount: 1 + - id: Soap + amount: 1 + - id: SoapHomemade + amount: 1 + - id: SoapDeluxe + amount: 1 + - type: Storage + maxItemSize: Normal + grid: + - 0,0,3,1 + whitelist: + tags: + - Soap + - type: Sprite + layers: + - state: box + +- type: entity + name: scented soap sampler pack + parent: BoxCardboard + id: BoxSoapsAssortedOmega + description: A box of various scented soaps. Ooh, bluespace. + components: + - type: StorageFill + contents: + - id: SoapNT + amount: 1 + - id: Soap + amount: 1 + - id: SoapOmega + amount: 1 + - id: SoapDeluxe + amount: 1 + - type: Storage + maxItemSize: Normal + grid: + - 0,0,3,1 + whitelist: + tags: + - Soap + - type: Sprite + layers: + - state: box + +- type: entity + name: Build-a-Buddy kit + suffix: Human + parent: BoxHug + id: BoxBuildABuddyHuman + description: "\"Henry the Human\" Build-a-Buddy kit. Some assembly required." + components: + - type: StorageFill + contents: + - id: HeadHuman + amount: 1 + - id: TorsoHuman + amount: 1 + - id: LeftArmHuman + amount: 1 + - id: RightArmHuman + amount: 1 + - id: LeftHandHuman + amount: 1 + - id: RightHandHuman + amount: 1 + - id: LeftLegHuman + amount: 1 + - id: RightLegHuman + amount: 1 + - id: LeftFootHuman + amount: 1 + - id: RightFootHuman + amount: 1 + - type: Storage + grid: + - 0,0,4,3 + whitelist: + components: + - BodyPart + +# DeltaV - Goblins Aren't Real +#- type: entity +# name: Build-a-Buddy kit +# suffix: Goblin +# parent: BoxBuildABuddyHuman +# id: BoxBuildABuddyGoblin +# description: "\"Greta the Goblin\" Build-a-Buddy kit. Some assembly required." +# components: +# - type: StorageFill +# contents: +# - id: HeadGoblin +# amount: 1 +# - id: TorsoGoblin +# amount: 1 +# - id: LeftArmGoblin +# amount: 1 +# - id: RightArmGoblin +# amount: 1 +# - id: LeftHandGoblin +# amount: 1 +# - id: RightHandGoblin +# amount: 1 +# - id: LeftLegGoblin +# amount: 1 +# - id: RightLegGoblin +# amount: 1 +# - id: LeftFootGoblin +# amount: 1 +# - id: RightFootGoblin +# amount: 1 + +- type: entity + name: Build-a-Buddy kit + suffix: Reptilian + parent: BoxBuildABuddyHuman + id: BoxBuildABuddyReptilian + description: "\"Randy the Reptilian\" Build-a-Buddy kit. Some assembly required." + components: + - type: StorageFill + contents: + - id: HeadReptilian + amount: 1 + - id: TorsoReptilian + amount: 1 + - id: LeftArmReptilian + amount: 1 + - id: RightArmReptilian + amount: 1 + - id: LeftHandReptilian + amount: 1 + - id: RightHandReptilian + amount: 1 + - id: LeftLegReptilian + amount: 1 + - id: RightLegReptilian + amount: 1 + - id: LeftFootReptilian + amount: 1 + - id: RightFootReptilian + amount: 1 + +- type: entity + name: Build-a-Buddy kit + suffix: Slime + parent: BoxBuildABuddyHuman + id: BoxBuildABuddySlime + description: "\"Steven the Slime\" Build-a-Buddy kit. Some assembly required." + components: + - type: StorageFill + contents: + - id: HeadSlime + amount: 1 + - id: TorsoSlime + amount: 1 + - id: LeftArmSlime + amount: 1 + - id: RightArmSlime + amount: 1 + - id: LeftHandSlime + amount: 1 + - id: RightHandSlime + amount: 1 + - id: LeftLegSlime + amount: 1 + - id: RightLegSlime + amount: 1 + - id: LeftFootSlime + amount: 1 + - id: RightFootSlime + amount: 1 + +- type: entity + name: Build-a-Buddy kit + suffix: Vulpkanin + parent: BoxBuildABuddyHuman + id: BoxBuildABuddyVulpkanin + description: "\"Valerie the Vulpkanin\" Build-a-Buddy kit. Some assembly required." + components: + - type: StorageFill + contents: + - id: HeadVulpkanin + amount: 1 + - id: TorsoVulpkanin + amount: 1 + - id: LeftArmVulpkanin + amount: 1 + - id: RightArmVulpkanin + amount: 1 + - id: LeftHandVulpkanin + amount: 1 + - id: RightHandVulpkanin + amount: 1 + - id: LeftLegVulpkanin + amount: 1 + - id: RightLegVulpkanin + amount: 1 + - id: LeftFootVulpkanin + amount: 1 + - id: RightFootVulpkanin + amount: 1 diff --git a/Resources/Prototypes/Entities/Objects/Specific/Mail/Items/misc.yml b/Resources/Prototypes/Entities/Objects/Specific/Mail/Items/misc.yml new file mode 100644 index 0000000000..6f1c86e00b --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Specific/Mail/Items/misc.yml @@ -0,0 +1,117 @@ +# Mail-only items. If/when these get used for anything else, please move them to another folder. +# Pranks: admin items or effects put into an envelope, released when opened or damaged. +- type: entity + id: DelayedSmoke + parent: BaseItem + noSpawn: true + name: delayed smoke + suffix: "(10s)" + components: + - type: Sprite #DeltaV: Apparently these want sprites, probably because they're baseitems + sprite: /Textures/Objects/Fun/goldbikehorn.rsi + visible: false + state: icon + - type: DelayedItem + item: AdminInstantEffectSmoke10 + +- type: entity + id: AdminInstantEffectEMP7 + noSpawn: true + suffix: EMP, 7 meters + parent: AdminInstantEffectBase + components: + - type: EmpOnTrigger + range: 7 + energyConsumption: 50000 + +- type: entity + id: DelayedEMP + parent: BaseItem + noSpawn: true + name: delayed EMP (7 meters) + components: + - type: Sprite #DeltaV: Apparently these want sprites, probably because they're baseitems + sprite: /Textures/Objects/Fun/goldbikehorn.rsi + visible: false + state: icon + - type: DelayedItem + item: AdminInstantEffectEMP7 + +# Miscellaneous Items + +- type: entity + id: SyringeCognizine + parent: Syringe + name: cognizine syringe + components: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 15 + reagents: + - ReagentId: Cognizine + Quantity: 15 # Surely three friends is enough. + +- type: entity + id: SyringeOpporozidone + parent: Syringe + name: opporozidone syringe + components: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 15 +# reagents: # TODO: we don't have that yet. Guess the people will receive an empty syringe instead. +# - ReagentId: Opporozidone +# Quantity: 15 + +- type: entity + id: NecrosolChemistryBottle + parent: BaseChemistryBottleFilled + name: necrosol bottle + components: + - type: SolutionContainerManager + solutions: + drink: + maxVol: 30 + reagents: + - ReagentId: Necrosol + Quantity: 30 + +# Premium Alcohol: wait, it's just marketing? +# TODO: different sprites would be nice. +- type: entity + id: DrinkPremiumVodkaBottleFull + parent: DrinkVodkaBottleFull + name: Moment of Clarity vodka bottle + description: When things get a bit hectic, all you need is a Moment of Clarity. + +- type: entity + id: DrinkPremiumGinBottleFull + parent: DrinkGinBottleFull + name: Harry's gin bottle + description: An interesting set of botanicals, for sure. Is that pumpkin? + +- type: entity + id: DrinkPremiumTequilaBottleFull + parent: DrinkTequilaBottleFull + name: Casa del Eorg tequila bottle + description: Save the best for last. Casa del Eorg, 100% agave. + +- type: entity + id: DrinkPremiumWhiskeyBottleFull + parent: DrinkWhiskeyBottleFull + name: Ol' Prowler 18 whiskey bottle + description: Surprisingly smooth, it has a nasty habit of sneaking up on you. + +- type: entity + id: DrinkPremiumRumBottleFull + parent: DrinkRumBottleFull + name: Redeemer's Bounty rum bottle + description: Well, you asked for it. Navy strength. + +- type: entity + id: DrinkPremiumAbsintheBottleFull + parent: DrinkAbsintheBottleFull + name: Bureaucracy's Kiss absinthe bottle + description: A refined taste that tends to linger. diff --git a/Resources/Prototypes/Entities/Objects/Specific/Mail/Items/paper.yml b/Resources/Prototypes/Entities/Objects/Specific/Mail/Items/paper.yml new file mode 100644 index 0000000000..b21ca40717 --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Specific/Mail/Items/paper.yml @@ -0,0 +1,483 @@ +# Papers (letters, ad copy) +# TODO: these should really be based on localization strings. +- type: entity + id: PaperMailNFPowerTool + name: Hazard Fraught advertisement + categories: [ HideSpawnMenu ] + suffix: "power tool ad, formatted" + parent: Paper + components: + - type: Paper + content: |2 + + [head=1]Hazard Fraught Tools[/head] + + [head=2]Discount Tools at Quality Prices![/head] + + [head=2]Fax us for a catalog at + [color=#990000]ERROR: UNEXPECTED EOF[/color][/head] + +- type: entity + id: PaperMailNFVagueThreat1 + categories: [ HideSpawnMenu ] + suffix: "vague mail threat 1, formatted" + parent: Paper + components: + - type: Paper + content: |2 + + [head=1]I know what you did.[/head] + + [head=3]You don't know what I'm going to do to you.[/head] + +- type: entity + id: PaperMailNFVagueThreat2 + categories: [ HideSpawnMenu ] + suffix: "vague mail threat 2, formatted" + parent: Paper + components: + - type: Paper + content: |2 + + [head=1]I'm coming for you.[/head] + +- type: entity + id: PaperMailNFVagueThreat3 + categories: [ HideSpawnMenu ] + suffix: "vague mail threat 3, formatted" + parent: Paper + components: + - type: Paper + content: |2 + + [head=1]You're next.[/head] + +- type: entity + id: PaperMailNFVagueThreat4 + categories: [ HideSpawnMenu ] + suffix: "vague mail threat 4, formatted" + parent: Paper + components: + - type: Paper + content: |2 + + [head=1]We see you.[/head] + +- type: entity + id: PaperMailNFVagueThreat5 + categories: [ HideSpawnMenu ] + suffix: "vague mail threat 5, formatted" + parent: Paper + components: + - type: Paper + content: |2 + + [head=2]I hope your affairs are in order.[/head] + +- type: entity + id: PaperMailNFVagueThreat6 + categories: [ HideSpawnMenu ] + suffix: "vague mail threat 6, formatted" + parent: Paper + components: + - type: Paper + content: |2 + + [head=1]It's only a matter of time.[/head] + + + [head=1]Enjoy it while it lasts.[/head] + +- type: entity + id: PaperMailNFVagueThreat7 + categories: [ HideSpawnMenu ] + suffix: "vague mail threat 7, formatted" + parent: Paper + components: + - type: Paper + content: |2 + + [head=2]Who should we mail your pieces to?[/head] + +- type: entity + id: PaperMailNFVagueThreat8 + categories: [ HideSpawnMenu ] + suffix: "vague mail threat 8, formatted" + parent: Paper + components: + - type: Paper + content: |2 + + [head=2]Would you prefer to die slowly or quickly? + [/head] + [head=1]Just kidding.[/head] + + [head=2]We don't care what you think.[/head] + +- type: entity + id: PaperMailNFVagueThreat9 + categories: [ HideSpawnMenu ] + suffix: "vague mail threat 9, formatted" + parent: Paper + components: + - type: Paper + content: |2 + + [head=3]I think your head would look nice on my mantel.[/head] + +- type: entity + id: PaperMailNFVagueThreat10 + categories: [ HideSpawnMenu ] + suffix: "vague mail threat 10, formatted" + parent: Paper + components: + - type: Paper + content: |2 + + [head=1]You should have paid up.[/head] + + + [head=1]It's too late now.[/head] + +- type: entity + id: PaperMailNFVagueThreat11 + categories: [ HideSpawnMenu ] + suffix: "vague mail threat 11, formatted" + parent: Paper + components: + - type: Paper + content: |2 + + [head=3]Your family will miss you, but don't worry.[/head] + + + [head=1]We'll take care of them too.[/head] + +- type: entity + id: PaperMailNFVagueThreat12 + categories: [ HideSpawnMenu ] + suffix: "vague mail threat 12, formatted" + parent: Paper + components: + - type: Paper + content: |2 + + [head=3]I have a bet that you're going to die today.[/head] + + + [head=1]I'm not afraid to cheat.[/head] + +- type: entity + id: PaperMailNFPwrGameAd + name: pwr game advertisement + categories: [ HideSpawnMenu ] + suffix: "pwr game ad" + parent: Paper + components: + - type: Paper + content: |2 + + [head=1]Drink Pwr Game![/head] + + [head=3]Proud sponsor of the NT Block Game Championship.[/head] + +- type: entity + id: PaperMailNFRedBoolAd + name: red bool advertisement + categories: [ HideSpawnMenu ] + suffix: "red bool ad" + parent: Paper + components: + - type: Paper + content: |2 + + [head=2]Try NEW Reformulated Red Bool![/head] + + [head=2]Over [color=#dd0000]1.5g[/color] of caffeine per can![/head] + + [head=2]Punch your heart into overdrive![/head] + +- type: entity + id: PaperMailNFSpaceColaAd + name: space cola advertisement + categories: [ HideSpawnMenu ] + suffix: "space cola ad" + parent: Paper + components: + - type: Paper + content: |2 + + [head=2]The classic taste you love, Space Cola.[/head] + + [head=2]Now certified lead-free.[/head] + +- type: entity + id: PaperMailNFSpaceMountainWindAd + name: space mountain wind advertisement + categories: [ HideSpawnMenu ] + suffix: "space mountain wind ad" + parent: Paper + components: + - type: Paper + content: |2 + + [head=3]When it's time to game, there's one choice:[/head] + + [head=1]Space Mountain Wind.[/head] + +- type: entity + id: PaperMailNFSpaceUpAd + name: space up advertisement + categories: [ HideSpawnMenu ] + suffix: "space up ad" + parent: Paper + components: + - type: Paper + content: |2 + + [head=3]The crisp, refreshing taste of lemon and lime.[/head] + + + [head=1]Space Up![/head] + + + [head=2]Ask your barkeep for a Sui Dream today![/head] + +- type: entity + id: PaperMailNTSoapAd1 + categories: [ HideSpawnMenu ] + suffix: "soap ad 1" + parent: Paper + components: + - type: Paper + stampedBy: + - stampedColor: '#333333FF' + stampedName: Christopher Cleanman +# stampType: Signature #DeltaV - Not compatible with our signatures code stuff apparently + content: |2 + [head=3]Hello Valued Customer,[/head] + You have been selected to receive a complimentary sampler of scented soaps that Nanotrasen has to offer. + + Why not enjoy a nice warm shower with our scented soaps? Tested and effective vs. vent crud and mold. + + We hope you enjoy. + + Sincerely, + Christopher Cleanman, Vice President, NT Habs - Toiletries Dept. + +- type: entity + id: PaperMailNTSoapAd2 + categories: [ HideSpawnMenu ] + suffix: "soap ad 2" #DeltaV - Edited to not be addressed to Frontier Citizens, localized + parent: Paper + components: + - type: Paper + content: |2 + [head=2]GREETINGS DELTA SECTOR CITIZEN[/head] + + OUR REPORTS INDICATE THAT: + 1. YOU HAVE FAILED YOUR QUARTERLY HYGIENE INSPECTION. + 2. THIS HAS REDUCED SECTOR EFFICIENCY BY [bold]0.02%[/bold]. + + ENCLOSED IS A SELECTION OF HYGIENE PRODUCTS SUITABLE FOR USE BY ORGANICS. WE HOPE THAT THIS SITUATION IS RESOLVED PROMPTLY. + + [italic]THIS IS AN AUTOMATED MESSAGE. DO NOT REPLY.[/italic] + +- type: entity + id: PaperMailNTConscript + categories: [ HideSpawnMenu ] + suffix: "conscript" + parent: Paper + components: + - type: Paper + content: |2 + + [head=1]NOT ONE STEP BACK.[/head] + + + [head=1]FOR THE FRONTIER.[/head] + +- type: entity + id: PaperMailNTMusket + categories: [ HideSpawnMenu ] + suffix: "musket" + parent: Paper + components: + - type: Paper + content: |2 + + [head=2]Use a musket for sector defense, + like the founding fathers intended.[/head] + +- type: entity + id: PaperMailNFPaperPusherAd + categories: [ HideSpawnMenu ] + suffix: "paper pusher" + parent: Paper + components: + - type: Paper + content: |2 + + [head=2]Here is a pen for any letters you write. + [/head] + [head=1]Keep it close, use it often.[/head] + + [head=2]May you write well, neatly, and with style.[/head] + + [head=3]Sincerely, + [italic]The Frontier Paper Pusher's Club[/italic][/head] + +- type: entity + id: PaperMailNFPetBedAssemblyManual + name: pet bed assembly manual + categories: [ HideSpawnMenu ] + suffix: "pet bed assembly manual" + parent: Paper + components: + - type: Paper + content: |2 + + [head=1]HÖGANÄS[/head] + + [italic](There is a black and white picture of a pet bed on the first page.)[/italic] + + [italic](On the next few pages, you see a list of materials and a happy stick figure assembling furniture.)[/italic] + + [italic](On the pages after that, you see a set of instructions to assemble a pet bed. You're sure you don't need them, how hard could it be?)[/italic] + +- type: entity + id: PaperMailNTBoxer + categories: [ HideSpawnMenu ] + suffix: "boxer" + parent: Paper + components: + - type: Paper + content: |2 + [head=2]You've gotta defend your belt, champ. + [/head] + [head=1]They're coming for you.[/head] + + [head=2]This should help. Knock 'em out.[/head] + +# Placeholder for an arm-on-use, flashbang fakeout pipebomb +- type: entity + id: PaperMailNFPipebombIntern + categories: [ HideSpawnMenu ] + suffix: "pipe bomb intern" + parent: Paper + components: + - type: Paper + stampedBy: + - stampedColor: '#333333FF' + stampedName: craig +# stampType: Signature #DeltaV - Not compatible with our signatures code stuff apparently + content: |2 + [bold]hey uh, they told me to send you a pipebomb i guess? + + this is all i could find around here, hope that works + + thanks[/bold] + +- type: entity + id: PaperMailNFAntivirus + name: Snortin Antivirus invoice + categories: [ HideSpawnMenu ] + suffix: "antivirus ad" + parent: Paper + components: + - type: Paper + content: |2 + + [head=1]Invoice[/head][head=3] + Snortin Antivirus Software[/head] + + [head=3]Order #41003 + [bold][bullet/][/bold] 1x Snortin Total-275 Antivirus Install Disk[/head] + + [head=3]Total: 947381 Spesos[/head] + + Thank you for making purchase from Snortin Antivirus Software. + We assuring you that our product is greatest. + Please sending payment at earliest convenience. + +- type: entity + id: PaperMailNFEMPPreparedness + categories: [ HideSpawnMenu ] + name: EMP preparedness response form + suffix: "emp preparedness" #DeltaV - Replaces mention of SR with HoS + parent: Paper + components: + - type: Paper + content: |2 + + [head=1]EMP Preparedness Response[/head] + + You have been selected to receive a NT EMP Preparedness kit as a test. Note that this is only a test. In a real emergency, follow the instructions of your vessel's command staff. + + As the recipient of this, please note [bold]any improvements[/bold] that could be made towards the EMP preparedness of the vessel you were aboard when opening and submit this form to your serving Captain or Head of Security. + + [bold]Date of test:[/bold] + [bold]Number of affected items:[/bold] + [bold]Perceived severity of incident:[/bold] + [bold]Suggested improvements:[/bold] + +- type: entity + id: PaperMailNFBuildABuddy + categories: [ HideSpawnMenu ] + name: Build-a-Buddy adoption letter + suffix: "build-a-buddy" #DeltaV- Body text changed, because Goblins Aren't Real + parent: Paper + components: + - type: Paper + stampState: paper_stamp-generic + stampedBy: + - stampedColor: '#FF6699FF' + stampedName: Chief Friendship Officer + - stampedColor: '#333333FF' + stampedName: Cuts-With-Scalpel +# stampType: Signature #DeltaV - Not compatible with our signatures code stuff apparently. + content: |2 + + [head=1]Note of Adoption[/head] + + You're now the proud owner of your very own Build-a-Buddy! + + We hope that your new friend can serve as a shoulder to lean on in the depths of space, and hopefully you won't be quite as lonely out there. Personally, I find putting them together to be rather therapeutic. + + [bold]Collect the whole set![/bold] + [bold][bullet/][/bold] Henry the Human + [bold][bullet/][/bold] Randy the Reptilian + [bold][bullet/][/bold] Steven the Slime + [bold][bullet/][/bold] Valerie the Vulpkanin + +- type: entity + id: PaperMailNFSpaceLaw + categories: [ HideSpawnMenu ] + suffix: "space-law" #DeltaV- edited contents to be from the Delta Sector instead of the Frontier + parent: Paper + components: + - type: Paper + stampState: paper_stamp-centcom + stampedBy: + - stampedColor: '#006600FF' + stampedName: Central Admiralty of the Delta Sector + content: |2 + + [head=1]Space Law is your shield.[/head] + + [head=2]With it, you guard the Delta Sector.[/head][head=3] + [/head] + [head=1]Memorize it. Grasp it firmly.[/head] + + [head=2]The SOP is your sword, don't get rusty.[/head] + + [head=2]Maintain your balance, and wield it well.[/head] + + + + + + + + + [head=3][italic]Internal Bureau of Propaganda[/italic][/head] diff --git a/Resources/Prototypes/Entities/Objects/Specific/Mail/base_mail_large.yml b/Resources/Prototypes/Entities/Objects/Specific/Mail/base_mail_large.yml new file mode 100644 index 0000000000..a5dcefacba --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Specific/Mail/base_mail_large.yml @@ -0,0 +1,79 @@ +# Large packages. +- type: entity + parent: BaseMail + abstract: true + id: BaseMailLarge + name: mail-large-item-name-unaddressed + components: + - type: Item + size: Ginormous + - type: Sprite + scale: 0.8, 0.8 + sprite: Objects/Specific/Mail/mail_large.rsi + layers: + - state: icon + map: ["enum.MailVisualLayers.Icon"] + - state: fragile + map: ["enum.MailVisualLayers.FragileStamp"] + visible: false + - map: ["enum.MailVisualLayers.JobStamp"] + scale: 0.8, 0.8 + offset: 0.235, -0.01 + - state: locked + map: ["enum.MailVisualLayers.Lock"] + - state: priority + map: ["enum.MailVisualLayers.PriorityTape"] + visible: false + shader: unshaded + - state: broken + map: ["enum.MailVisualLayers.Breakage"] + visible: false + - type: GenericVisualizer + visuals: + enum.MailVisuals.IsTrash: + enum.MailVisualLayers.Icon: + True: + state: trash + False: + state: icon + enum.MailVisuals.IsLocked: + enum.MailVisualLayers.Lock: + True: + visible: true + False: + visible: false + enum.MailVisuals.IsFragile: + enum.MailVisualLayers.FragileStamp: + True: + visible: true + False: + visible: false + enum.MailVisuals.IsPriority: + enum.MailVisualLayers.PriorityTape: + True: + visible: true + False: + visible: false + enum.MailVisuals.IsPriorityInactive: + enum.MailVisualLayers.PriorityTape: + True: + shader: shaded + state: priority_inactive + False: + shader: unshaded + state: priority + enum.MailVisuals.IsBroken: + enum.MailVisualLayers.Breakage: + True: + visible: true + False: + visible: false + - type: MultiHandedItem + - type: Mail + isLarge: true + +- type: entity + noSpawn: true + parent: BaseMailLarge + id: MailLargeAdminFun + suffix: adminfun diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml index 594ffb4d4d..62ed28e114 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml @@ -375,3 +375,29 @@ tags: - CartridgeRocket proto: ImmovableRodSlow + +# Frontier mail RPDS +- type: entity + name: mail RPDS + parent: WeaponLauncherChinaLake + id: WeaponMailLake + description: Rap(b?)id Parcel Delivery System + components: + - type: Sprite + sprite: Objects/Weapons/Guns/Launchers/mail.rsi + layers: + - state: icon + map: ["enum.GunVisualLayers.Base"] + - type: Clothing + sprite: Objects/Weapons/Guns/Launchers/mail.rsi + quickEquip: false + slots: + - Back + - Belt + - suitStorage + - type: BallisticAmmoProvider + proto: null + whitelist: + tags: + - MailCapsule + capacity: 4 diff --git a/Resources/Prototypes/Loadouts/Categories/categories.yml b/Resources/Prototypes/Loadouts/Categories/categories.yml index 94e90b663f..c47d6ddef8 100644 --- a/Resources/Prototypes/Loadouts/Categories/categories.yml +++ b/Resources/Prototypes/Loadouts/Categories/categories.yml @@ -65,6 +65,9 @@ subCategories: - JobsServiceUncategorized - JobsServiceBartender + - JobsServiceBotanist + - JobsServiceChef + - JobsServiceJanitor - type: loadoutCategory id: JobsServiceUncategorized @@ -72,6 +75,15 @@ - type: loadoutCategory id: JobsServiceBartender +- type: loadoutCategory + id: JobsServiceBotanist + +- type: loadoutCategory + id: JobsServiceChef + +- type: loadoutCategory + id: JobsServiceJanitor + - type: loadoutCategory id: Mask root: true diff --git a/Resources/Prototypes/Loadouts/Jobs/science.yml b/Resources/Prototypes/Loadouts/Jobs/science.yml index 9a9526f83f..198d7f9cc6 100644 --- a/Resources/Prototypes/Loadouts/Jobs/science.yml +++ b/Resources/Prototypes/Loadouts/Jobs/science.yml @@ -3,14 +3,14 @@ - type: loadout id: LoadoutScienceUniformJumpskirtSenior category: JobsScience - cost: 1 + cost: 0 exclusive: true requirements: - !type:CharacterItemGroupRequirement group: LoadoutUniformsScience - - !type:CharacterJobRequirement - jobs: - - Scientist + - !type:CharacterDepartmentRequirement + departments: + - Epistemics - !type:CharacterDepartmentTimeRequirement department: Epistemics min: 216000 # 60 hours @@ -20,14 +20,14 @@ - type: loadout id: LoadoutScienceUniformJumpsuitSenior category: JobsScience - cost: 1 + cost: 0 exclusive: true requirements: - !type:CharacterItemGroupRequirement group: LoadoutUniformsScience - - !type:CharacterJobRequirement - jobs: - - Scientist + - !type:CharacterDepartmentRequirement + departments: + - Epistemics - !type:CharacterDepartmentTimeRequirement department: Epistemics min: 216000 # 60 hours @@ -37,28 +37,28 @@ - type: loadout id: LoadoutScienceUniformJumpskirtRoboticist category: JobsScience - cost: 1 + cost: 0 exclusive: true requirements: - !type:CharacterItemGroupRequirement group: LoadoutUniformsScience - - !type:CharacterJobRequirement - jobs: - - Scientist + - !type:CharacterDepartmentRequirement + departments: + - Epistemics items: - ClothingUniformJumpskirtRoboticist - type: loadout id: LoadoutScienceUniformJumpsuitRoboticist category: JobsScience - cost: 1 + cost: 0 exclusive: true requirements: - !type:CharacterItemGroupRequirement group: LoadoutUniformsScience - - !type:CharacterJobRequirement - jobs: - - Scientist + - !type:CharacterDepartmentRequirement + departments: + - Epistemics items: - ClothingUniformJumpsuitRoboticist @@ -100,11 +100,9 @@ requirements: - !type:CharacterItemGroupRequirement group: LoadoutOuterScience - - !type:CharacterJobRequirement - jobs: - - Scientist - - ResearchAssistant - - ResearchDirector + - !type:CharacterDepartmentRequirement + departments: + - Epistemics items: - ClothingOuterCoatRnd @@ -116,11 +114,9 @@ requirements: - !type:CharacterItemGroupRequirement group: LoadoutOuterScience - - !type:CharacterJobRequirement - jobs: - - Scientist - - ResearchAssistant - - ResearchDirector + - !type:CharacterDepartmentRequirement + departments: + - Epistemics items: - ClothingOuterCoatLab @@ -132,37 +128,37 @@ requirements: - !type:CharacterItemGroupRequirement group: LoadoutOuterScience - - !type:CharacterJobRequirement - jobs: - - Scientist + - !type:CharacterDepartmentRequirement + departments: + - Epistemics items: - ClothingOuterCoatRobo - type: loadout id: LoadoutScienceOuterWinterSci category: JobsScience - cost: 1 + cost: 0 exclusive: true requirements: - !type:CharacterItemGroupRequirement group: LoadoutOuterScience - - !type:CharacterJobRequirement - jobs: - - Scientist + - !type:CharacterDepartmentRequirement + departments: + - Epistemics items: - ClothingOuterWinterSci - type: loadout id: LoadoutScienceOuterLabcoatSeniorResearcher category: JobsScience - cost: 1 + cost: 0 exclusive: true requirements: - !type:CharacterItemGroupRequirement group: LoadoutOuterScience - - !type:CharacterJobRequirement - jobs: - - Scientist + - !type:CharacterDepartmentRequirement + departments: + - Epistemics - !type:CharacterDepartmentTimeRequirement department: Epistemics min: 216000 # 60 hours @@ -177,11 +173,9 @@ requirements: - !type:CharacterItemGroupRequirement group: LoadoutOuterScience - - !type:CharacterJobRequirement - jobs: - - Scientist - - ResearchAssistant - - ResearchDirector + - !type:CharacterDepartmentRequirement + departments: + - Epistemics items: - ClothingOuterExplorerCoat @@ -265,10 +259,9 @@ requirements: - !type:CharacterItemGroupRequirement group: LoadoutGlovesScience - - !type:CharacterJobRequirement - jobs: - - Scientist - - ResearchDirector + - !type:CharacterDepartmentRequirement + departments: + - Epistemics items: - ClothingHandsGlovesColorPurple @@ -280,10 +273,9 @@ requirements: - !type:CharacterItemGroupRequirement group: LoadoutGlovesScience - - !type:CharacterJobRequirement - jobs: - - Scientist - - ResearchDirector + - !type:CharacterDepartmentRequirement + departments: + - Epistemics items: - ClothingHandsGlovesLatex @@ -295,10 +287,9 @@ requirements: - !type:CharacterItemGroupRequirement group: LoadoutGlovesScience - - !type:CharacterJobRequirement - jobs: - - Scientist - - ResearchDirector + - !type:CharacterDepartmentRequirement + departments: + - Epistemics items: - ClothingHandsGlovesRobohands @@ -312,10 +303,9 @@ requirements: - !type:CharacterItemGroupRequirement group: LoadoutNeckScience - - !type:CharacterJobRequirement - jobs: - - Scientist - - ResearchDirector + - !type:CharacterDepartmentRequirement + departments: + - Epistemics items: - ClothingNeckTieSci @@ -327,10 +317,9 @@ requirements: - !type:CharacterItemGroupRequirement group: LoadoutNeckScience - - !type:CharacterJobRequirement - jobs: - - Scientist - - ResearchDirector + - !type:CharacterDepartmentRequirement + departments: + - Epistemics items: - ClothingNeckScarfStripedPurple @@ -356,9 +345,9 @@ requirements: - !type:CharacterItemGroupRequirement group: LoadoutNeckScience - - !type:CharacterJobRequirement - jobs: - - Chaplain + - !type:CharacterDepartmentRequirement + departments: + - Epistemics items: - ClothingNeckScarfStripedBlack @@ -388,11 +377,9 @@ requirements: - !type:CharacterItemGroupRequirement group: LoadoutHeadScience - - !type:CharacterJobRequirement - jobs: - - Scientist - - ResearchAssistant - - ResearchDirector + - !type:CharacterDepartmentRequirement + departments: + - Epistemics items: - ClothingHeadHatBeretRND @@ -404,9 +391,9 @@ requirements: - !type:CharacterItemGroupRequirement group: LoadoutHeadScience - - !type:CharacterJobRequirement - jobs: - - Chaplain + - !type:CharacterDepartmentRequirement + departments: + - Epistemics items: - ClothingHeadHatFez @@ -476,11 +463,9 @@ requirements: - !type:CharacterItemGroupRequirement group: LoadoutEyesScience - - !type:CharacterJobRequirement - jobs: - - Scientist - - ResearchAssistant - - ResearchDirector + - !type:CharacterDepartmentRequirement + departments: + - Epistemics items: - ClothingEyesHudDiagnostic @@ -492,11 +477,9 @@ requirements: - !type:CharacterItemGroupRequirement group: LoadoutEyesScience - - !type:CharacterJobRequirement - jobs: - - Scientist - - ResearchAssistant - - ResearchDirector + - !type:CharacterDepartmentRequirement + departments: + - Epistemics items: - ClothingEyesEyepatchHudDiag @@ -510,11 +493,9 @@ requirements: - !type:CharacterItemGroupRequirement group: LoadoutShoesScience - - !type:CharacterJobRequirement - jobs: - - Scientist - - ResearchAssistant - - ResearchDirector + - !type:CharacterDepartmentRequirement + departments: + - Epistemics items: - ClothingShoesBootsWinterSci @@ -529,11 +510,9 @@ requirements: - !type:CharacterItemGroupRequirement group: LoadoutOuterScience - - !type:CharacterJobRequirement - jobs: - - Scientist - - ResearchAssistant - - ResearchDirector + - !type:CharacterDepartmentRequirement + departments: + - Epistemics - type: loadout id: LoadoutHeadHoodTechPriest @@ -545,8 +524,105 @@ requirements: - !type:CharacterItemGroupRequirement group: LoadoutHeadScience + - !type:CharacterDepartmentRequirement + departments: + - Epistemics + +# Cataloguer +- type: loadout + id: LoadoutScienceJumpsuitLibrarianNt + category: JobsScience + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutCataloguerUniforms + - !type:CharacterJobRequirement + jobs: + - Librarian + items: + - ClothingUniformJumpsuitLibrarianNt + +- type: loadout + id: LoadoutScienceJumpsuitLibrarianIdris + category: JobsScience + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutCataloguerUniforms + - !type:CharacterJobRequirement + jobs: + - Librarian + items: + - ClothingUniformJumpsuitLibrarianIdris + +- type: loadout + id: LoadoutScienceJumpsuitLibrarianOrion + category: JobsScience + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutCataloguerUniforms + - !type:CharacterJobRequirement + jobs: + - Librarian + items: + - ClothingUniformJumpsuitLibrarianOrion + +- type: loadout + id: LoadoutScienceJumpsuitLibrarianHeph + category: JobsScience + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutCataloguerUniforms - !type:CharacterJobRequirement jobs: - - Scientist - - ResearchAssistant - - ResearchDirector + - Librarian + items: + - ClothingUniformJumpsuitLibrarianHeph + +- type: loadout + id: LoadoutScienceJumpsuitLibrarianPMCG + category: JobsScience + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutCataloguerUniforms + - !type:CharacterJobRequirement + jobs: + - Librarian + items: + - ClothingUniformJumpsuitLibrarianPMCG + +- type: loadout + id: LoadoutScienceJumpsuitLibrarianZav + category: JobsScience + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutCataloguerUniforms + - !type:CharacterJobRequirement + jobs: + - Librarian + items: + - ClothingUniformJumpsuitLibrarianZav + +- type: loadout + id: LoadoutScienceJumpsuitLibrarianZeng + category: JobsScience + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutCataloguerUniforms + - !type:CharacterJobRequirement + jobs: + - Librarian + items: + - ClothingUniformJumpsuitLibrarianZeng \ No newline at end of file diff --git a/Resources/Prototypes/Loadouts/Jobs/service.yml b/Resources/Prototypes/Loadouts/Jobs/service.yml index bdfa3d8165..b346bc3182 100644 --- a/Resources/Prototypes/Loadouts/Jobs/service.yml +++ b/Resources/Prototypes/Loadouts/Jobs/service.yml @@ -280,7 +280,7 @@ - type: loadout id: LoadoutServiceBartenderBoxLightRifleRubber category: JobsServiceBartender - cost: 1 + cost: 0 exclusive: true requirements: - !type:CharacterItemGroupRequirement @@ -308,7 +308,7 @@ - type: loadout id: LoadoutServiceBartenderMosinRubber category: JobsServiceBartender - cost: 3 + cost: 0 exclusive: true requirements: - !type:CharacterItemGroupRequirement @@ -319,10 +319,136 @@ items: - WeaponSniperMosinRubber +- type: loadout + id: LoadoutServiceJumpsuitBartenderNt + category: JobsServiceBartender + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutBartenderUniforms + - !type:CharacterJobRequirement + jobs: + - Bartender + items: + - ClothingUniformJumpsuitBartenderNt + +- type: loadout + id: LoadoutServiceJumpsuitBartenderIdris + category: JobsServiceBartender + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutBartenderUniforms + - !type:CharacterJobRequirement + jobs: + - Bartender + items: + - ClothingUniformJumpsuitBartenderIdris + +- type: loadout + id: LoadoutServiceJumpsuitBartenderOrion + category: JobsServiceBartender + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutBartenderUniforms + - !type:CharacterJobRequirement + jobs: + - Bartender + items: + - ClothingUniformJumpsuitBartenderOrion + +- type: loadout + id: LoadoutServiceHeadBartenderNt + category: JobsServiceBartender + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutBartenderHead + - !type:CharacterJobRequirement + jobs: + - Bartender + items: + - ClothingHeadHatFlatcapBartenderNanotrasen + +- type: loadout + id: LoadoutServiceHeadBartenderIdris + category: JobsServiceBartender + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutBartenderHead + - !type:CharacterJobRequirement + jobs: + - Bartender + items: + - ClothingHeadHatFlatcapBartenderIdris + +- type: loadout + id: LoadoutServiceHeadBartenderOrion + category: JobsServiceBartender + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutBartenderHead + - !type:CharacterJobRequirement + jobs: + - Bartender + items: + - ClothingHeadHatFlatcapBartenderOrion + +- type: loadout + id: LoadoutServiceOuterBartenderNt + category: JobsServiceBartender + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutBartenderOuterwear + - !type:CharacterJobRequirement + jobs: + - Bartender + items: + - ClothingOuterVestNt + +- type: loadout + id: LoadoutServiceOuterBartenderIdris + category: JobsServiceBartender + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutBartenderOuterwear + - !type:CharacterJobRequirement + jobs: + - Bartender + items: + - ClothingOuterVestIdris + +- type: loadout + id: LoadoutServiceOuterBartenderOrion + category: JobsServiceBartender + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutBartenderOuterwear + - !type:CharacterJobRequirement + jobs: + - Bartender + items: + - ClothingOuterVestOrion + # Botanist - type: loadout id: LoadoutServiceBotanistUniformOveralls - category: JobsServiceUncategorized + category: JobsServiceBotanist cost: 0 exclusive: true requirements: @@ -334,6 +460,48 @@ items: - ClothingUniformOveralls +- type: loadout + id: LoadoutServiceJumpsuitHydroponicsNt + category: JobsServiceBotanist + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutBotanistUniforms + - !type:CharacterJobRequirement + jobs: + - Botanist + items: + - ClothingUniformJumpsuitHydroponicsNt + +- type: loadout + id: LoadoutServiceJumpsuitHydroponicsIdris + category: JobsServiceBotanist + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutBotanistUniforms + - !type:CharacterJobRequirement + jobs: + - Botanist + items: + - ClothingUniformJumpsuitHydroponicsIdris + +- type: loadout + id: LoadoutServiceJumpsuitHydroponicsOrion + category: JobsServiceBotanist + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutBotanistUniforms + - !type:CharacterJobRequirement + jobs: + - Botanist + items: + - ClothingUniformJumpsuitHydroponicsOrion + # Lawyer - type: loadout id: LoadoutServiceLawyerUniformBlueSuit @@ -658,4 +826,174 @@ jobs: - Musician items: - - OcarinaInstrument \ No newline at end of file + - OcarinaInstrument + +# Chef +- type: loadout + id: LoadoutServiceJumpsuitChefNt + category: JobsServiceChef + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutChefUniforms + - !type:CharacterJobRequirement + jobs: + - Chef + items: + - ClothingUniformJumpsuitChefNt + +- type: loadout + id: LoadoutServiceJumpsuitChefIdris + category: JobsServiceChef + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutChefUniforms + - !type:CharacterJobRequirement + jobs: + - Chef + items: + - ClothingUniformJumpsuitChefIdris + +- type: loadout + id: LoadoutServiceJumpsuitChefOrion + category: JobsServiceChef + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutChefUniforms + - !type:CharacterJobRequirement + jobs: + - Chef + items: + - ClothingUniformJumpsuitChefOrion + +- type: loadout + id: LoadoutServiceHeadChefNt + category: JobsServiceChef + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutChefHead + - !type:CharacterJobRequirement + jobs: + - Chef + items: + - ClothingHeadHatChefNt + +- type: loadout + id: LoadoutServiceHeadChefIdris + category: JobsServiceChef + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutChefHead + - !type:CharacterJobRequirement + jobs: + - Chef + items: + - ClothingHeadHatChefIdris + +- type: loadout + id: LoadoutServiceHeadChefOrion + category: JobsServiceChef + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutChefHead + - !type:CharacterJobRequirement + jobs: + - Chef + items: + - ClothingHeadHatChefOrion + +- type: loadout + id: LoadoutServiceOuterChefNt + category: JobsServiceChef + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutChefOuter + - !type:CharacterJobRequirement + jobs: + - Chef + items: + - ClothingOuterJacketChefNt + +- type: loadout + id: LoadoutServiceOuterChefIdris + category: JobsServiceChef + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutChefOuter + - !type:CharacterJobRequirement + jobs: + - Chef + items: + - ClothingOuterJacketChefIdris + +- type: loadout + id: LoadoutServiceOuterChefOrion + category: JobsServiceChef + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutChefOuter + - !type:CharacterJobRequirement + jobs: + - Chef + items: + - ClothingOuterJacketChefOrion + +# Janitor +- type: loadout + id: LoadoutServiceJumpsuitJanitorNt + category: JobsServiceJanitor + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutJanitorUniforms + - !type:CharacterJobRequirement + jobs: + - Janitor + items: + - ClothingUniformJumpsuitJanitorNt + +- type: loadout + id: LoadoutServiceJumpsuitJanitorIdris + category: JobsServiceJanitor + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutJanitorUniforms + - !type:CharacterJobRequirement + jobs: + - Janitor + items: + - ClothingUniformJumpsuitJanitorIdris + +- type: loadout + id: LoadoutServiceJumpsuitJanitorOrion + category: JobsServiceJanitor + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutJanitorUniforms + - !type:CharacterJobRequirement + jobs: + - Janitor + items: + - ClothingUniformJumpsuitJanitorOrion \ No newline at end of file diff --git a/Resources/Prototypes/Nyanotrasen/Catalog/Fills/Vending/Inventories/maildrobe.yml b/Resources/Prototypes/Nyanotrasen/Catalog/Fills/Vending/Inventories/maildrobe.yml index 579c57ced4..2764255824 100644 --- a/Resources/Prototypes/Nyanotrasen/Catalog/Fills/Vending/Inventories/maildrobe.yml +++ b/Resources/Prototypes/Nyanotrasen/Catalog/Fills/Vending/Inventories/maildrobe.yml @@ -3,6 +3,8 @@ startingInventory: ClothingUniformMailCarrier: 2 ClothingUniformSkirtMailCarrier: 2 + WeaponMailLake: 1 # Frontier + BoxMailCapsulePrimed: 2 # Frontier ClothingHeadMailCarrier: 2 MailBag: 2 ClothingHeadsetCargo: 2 diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Devices/pda.yml b/Resources/Prototypes/Nyanotrasen/Entities/Objects/Devices/pda.yml index d898124b77..c85255f814 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Devices/pda.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Objects/Devices/pda.yml @@ -32,7 +32,7 @@ accentVColor: "#DFDFDF" - type: Icon state: pda-security - - type: CartridgeLoader # DeltaV - Crime Assist + SecWatch + - type: CartridgeLoader # Adds Crime Assist and SecWatch preinstalled: - CrewManifestCartridge - NotekeeperCartridge @@ -43,7 +43,7 @@ - type: entity parent: BasePDA id: MailCarrierPDA - name: courier PDA # DeltaV - Mail Carrier to Courier replacement + name: courier PDA description: Smells like unopened letters. components: - type: Sprite @@ -67,6 +67,12 @@ - type: Icon sprite: DeltaV/Objects/Devices/pda.rsi state: pda-mailcarrier + - type: CartridgeLoader # Adds a courier performance tracker + preinstalled: + - CrewManifestCartridge + - NotekeeperCartridge + - NewsReaderCartridge + - MailMetricsCartridge - type: entity parent: BasePDA diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Mail/base_mail.yml b/Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Mail/base_mail.yml index c1ca2cd60b..fd77f19dcc 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Mail/base_mail.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Mail/base_mail.yml @@ -5,11 +5,13 @@ name: mail-item-name-unaddressed components: - type: Item - size: Normal +# size: Normal # Frontier + storedRotation: -90 - type: Mail - type: AccessReader - type: Sprite - sprite: Nyanotrasen/Objects/Specific/Mail/mail.rsi + scale: 0.7, 0.7 # Frontier + sprite: Objects/Specific/Mail/mail.rsi layers: - state: icon map: ["enum.MailVisualLayers.Icon"] @@ -18,8 +20,8 @@ map: ["enum.MailVisualLayers.FragileStamp"] visible: false - map: ["enum.MailVisualLayers.JobStamp"] - scale: 0.5, 0.5 - offset: 0.275, 0.2 + scale: 0.8, 0.8 # Frontier 0.5<0.8 + offset: 0.225, 0.165 # Frontier (0.275, 0.2)<(0.225, 0.165) - state: locked map: ["enum.MailVisualLayers.Lock"] - state: priority @@ -92,6 +94,16 @@ damage: types: Blunt: 10 + - type: CargoSellBlacklist # Frontier + - type: Food # Frontier - Moth food + requiresSpecialDigestion: true + - type: SolutionContainerManager + solutions: + food: + maxVol: 1 + reagents: + - ReagentId: Nothing + Quantity: 1 # This empty parcel is allowed to exist and evade the tests for the admin # mailto command. diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Mail/mail.yml b/Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Mail/mail.yml deleted file mode 100644 index 7f1b26d9cb..0000000000 --- a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Mail/mail.yml +++ /dev/null @@ -1,835 +0,0 @@ -- type: entity - noSpawn: true - parent: BaseMail - id: MailAlcohol - suffix: alcohol - components: - - type: Mail - contents: - - id: DrinkAbsintheBottleFull - orGroup: Drink - - id: DrinkBlueCuracaoBottleFull - orGroup: Drink - - id: DrinkGinBottleFull - orGroup: Drink - - id: DrinkMelonLiquorBottleFull - orGroup: Drink - - id: DrinkRumBottleFull - orGroup: Drink - - id: DrinkTequilaBottleFull - orGroup: Drink - - id: DrinkVermouthBottleFull - orGroup: Drink - - id: DrinkVodkaBottleFull - orGroup: Drink - - id: DrinkWineBottleFull - orGroup: Drink - - id: DrinkGlass - amount: 2 - -- type: entity - noSpawn: true - parent: BaseMail - id: MailSake - suffix: osake - components: - - type: Mail - contents: - - id: DrinkSakeCup - amount: 2 - - id: DrinkTokkuri - -- type: entity - noSpawn: true - parent: BaseMail - id: MailAMEGuide - suffix: ameguide - components: - - type: Mail - contents: - - id: PaperWrittenAMEScribbles - - id: Pen - -- type: entity - noSpawn: true - parent: BaseMail - id: MailBible - suffix: bible - components: - - type: Mail - contents: - - id: Bible - -- type: entity - noSpawn: true - parent: BaseMail - id: MailBikeHorn - suffix: bike horn - components: - - type: Mail - contents: - - id: BikeHorn - -- type: entity - noSpawn: true - parent: BaseMail - id: MailBlockGameDIY - suffix: blockgamediy - components: - - type: Mail - contents: - - id: BlockGameArcadeComputerCircuitboard - -#- type: entity -# noSpawn: true -# parent: BaseMail -# id: MailBooks -# suffix: books -# components: -# - type: Mail -# contents: -# # Don't use BookDemonomiconRandom. -# # It uses a RandomSpawner which just spawns the book outside of the mail. -# - id: BookDemonomicon1 -# orGroup: Demonomicon -# - id: BookDemonomicon2 -# orGroup: Demonomicon -# - id: BookDemonomicon3 -# orGroup: Demonomicon -# # There's no way to signal "spawn nothing" with an orGroup, -# # so have this blank book instead. Write your own demon summoning tome! -# - id: BookRandom -# prob: 3 -# orGroup: Demonomicon -# - id: BookChemistryInsane -# prob: 0.10 -# - id: BookBotanicalTextbook -# prob: 0.5 -# - id: BookFishing -# prob: 0.10 -# - id: BookDetective -# prob: 0.10 -# - id: BookGnominomicon -# prob: 0.2 -# - id: BookFishops # DeltaV - fishops book -# prob: 0.2 -# - id: BookSalvageEpistemics1 -# prob: 0.025 - -- type: entity - noSpawn: true - parent: BaseMail - id: MailCake - suffix: cake - components: - - type: Mail - isFragile: true - isPriority: true - contents: - - id: FoodCakeBlueberry - orGroup: Cake - - id: FoodCakeCarrot - orGroup: Cake - - id: FoodCakeCheese - orGroup: Cake - - id: FoodCakeChocolate - orGroup: Cake - - id: FoodCakeChristmas - orGroup: Cake - - id: FoodCakeClown - orGroup: Cake - - id: FoodCakeLemon - orGroup: Cake - - id: FoodCakeLime - orGroup: Cake - - id: FoodCakeOrange - orGroup: Cake - - id: FoodCakePumpkin - orGroup: Cake - - id: FoodCakeVanilla - orGroup: Cake - - id: FoodMothMothmallow - orGroup: Cake - prob: 0.5 - - id: KnifePlastic - - id: ForkPlastic - amount: 2 - -- type: entity - noSpawn: true - parent: BaseMail - id: MailCallForHelp - suffix: call-for-help - components: - - type: Mail - contents: - - id: PaperMailCallForHelp1 - orGroup: Paper - - id: PaperMailCallForHelp2 - orGroup: Paper - - id: PaperMailCallForHelp3 - orGroup: Paper - - id: PaperMailCallForHelp4 - orGroup: Paper - - id: PaperMailCallForHelp5 - orGroup: Paper - - id: FlashlightLantern - orGroup: Gift - - id: Crowbar - orGroup: Gift - prob: 0.5 - - id: CrowbarRed - orGroup: Gift - prob: 0.5 - - id: ClothingMaskGas - orGroup: Gift - - id: WeaponFlareGun - orGroup: Gift - prob: 0.25 - -- type: entity - noSpawn: true - parent: BaseMail - id: MailCheese - suffix: cheese - components: - - type: Mail - isFragile: true - isPriority: true - contents: - - id: FoodCheese - - id: KnifePlastic - -- type: entity - noSpawn: true - parent: BaseMail - id: MailChocolate - suffix: chocolate - components: - - type: Mail - contents: - # TODO make some actual chocolate candy items. - - id: FoodSnackChocolate - amount: 3 - -- type: entity - noSpawn: true - parent: BaseMail - id: MailCigarettes - suffix: cigs - components: - - type: Mail - contents: - - id: CigPackRed - - id: CheapLighter - -- type: entity - noSpawn: true - parent: BaseMail - id: MailCigars - suffix: Cigars - components: - - type: Mail - contents: - - id: CigarCase - - id: Lighter - -- type: entity - noSpawn: true - parent: BaseMail - id: MailCookies - suffix: cookies - components: - - type: Mail - # What, you want to eat stale cookies? - isPriority: true - contents: - - id: FoodBakedCookie - - id: FoodBakedCookieOatmeal - - id: FoodBakedCookieRaisin - - id: FoodBakedCookieSugar - -- type: entity - noSpawn: true - parent: BaseMail - id: MailCosplayArc - suffix: cosplay-arc - components: - - type: Mail - openSound: /Audio/Nyanotrasen/Voice/Felinid/cat_wilhelm.ogg - contents: - - id: ClothingCostumeArcDress - -- type: entity - noSpawn: true - parent: BaseMail - id: MailCosplayGeisha - suffix: cosplay-geisha - components: - - type: Mail - contents: - - id: UniformGeisha - - id: DrinkTeapot - - id: DrinkTeacup - amount: 3 - -- type: entity - noSpawn: true - parent: BaseMail - id: MailCosplayMaid - suffix: cosplay-maid - components: - - type: Mail - contents: - - id: UniformMaid - - id: SprayBottleSpaceCleaner - -- type: entity - noSpawn: true - parent: BaseMail - id: MailCosplayNurse - suffix: cosplay-nurse - components: - - type: Mail - contents: - - id: ClothingUniformJumpskirtNurse - - id: Syringe - -- type: entity - noSpawn: true - parent: BaseMail - id: MailCosplaySchoolgirl - suffix: cosplay-schoolgirl - components: - - type: Mail - contents: - - id: UniformSchoolgirlBlack - orGroup: Color - - id: UniformSchoolgirlBlue - orGroup: Color - - id: UniformSchoolgirlCyan - orGroup: Color - - id: UniformSchoolgirlGreen - orGroup: Color - - id: UniformSchoolgirlOrange - orGroup: Color - - id: UniformSchoolgirlPink - orGroup: Color - - id: UniformSchoolgirlPurple - orGroup: Color - - id: UniformSchoolgirlRed - orGroup: Color - -- type: entity - noSpawn: true - parent: BaseMail - id: MailCosplayWizard - suffix: cosplay-wizard - components: - - type: Mail - contents: - - id: ClothingOuterWizardFake - - id: ClothingHeadHatWizardFake - - id: ClothingShoesWizardFake - -- type: entity - noSpawn: true - parent: BaseMail - id: MailCrayon - suffix: Crayon - components: - - type: Mail - contents: - - id: CrayonBox - -- type: entity - noSpawn: true - parent: BaseMail - id: MailFigurine - suffix: figurine - components: - - type: Mail - isFragile: true - contents: - - id: ToyAi - orGroup: Toy - - id: ToyNuke - orGroup: Toy - - id: ToyFigurinePassenger - orGroup: Toy - - id: ToyGriffin - orGroup: Toy - - id: ToyHonk - orGroup: Toy - - id: ToyIan - orGroup: Toy - - id: ToyMarauder - orGroup: Toy - - id: ToyMauler - orGroup: Toy - - id: ToyGygax - orGroup: Toy - - id: ToyOdysseus - orGroup: Toy - - id: ToyOwlman - orGroup: Toy - - id: ToyDeathRipley - orGroup: Toy - - id: ToyPhazon - orGroup: Toy - - id: ToyFireRipley - orGroup: Toy - - id: ToyReticence - orGroup: Toy - - id: ToyRipley - orGroup: Toy - - id: ToySeraph - orGroup: Toy - - id: ToyDurand - orGroup: Toy - -- type: entity - noSpawn: true - parent: BaseMail - id: MailFishingCap - suffix: fishingcap - components: - - type: Mail - contents: - - id: ClothingHeadFishCap - -- type: entity - noSpawn: true - parent: BaseMail - id: MailFlashlight - suffix: Flashlight - components: - - type: Mail - contents: - - id: FlashlightLantern - -#- type: entity -# noSpawn: true -# parent: BaseMail -# id: MailFlowers -# suffix: flowers -# components: -# - type: Mail -# contents: -# # TODO actual flowers -# - id: ClothingHeadHatFlowerCrown -# orGroup: Flower -# - id: ClothingHeadHatHairflower -# orGroup: Flower - -- type: entity - noSpawn: true - parent: BaseMail - id: MailHighlander - suffix: highlander - components: - - type: Mail - contents: - - id: ClothingUniformJumpskirtColorRed - - id: ClothingHeadHatBeret - - id: DrinkRedMeadGlass - - id: Claymore - -- type: entity - noSpawn: true - parent: BaseMail - id: MailHighlanderDulled - suffix: highlander, dulled - components: - - type: Mail - contents: - - id: ClothingUniformJumpskirtColorRed - - id: ClothingHeadHatBeret - - id: DrinkGlass - - id: ClaymoreDulled - -- type: entity - noSpawn: true - parent: BaseMail - id: MailHoneyBuns - suffix: honeybuns - components: - - type: Mail - contents: - - id: FoodBakedBunHoney - amount: 2 - -- type: entity - noSpawn: true - parent: BaseMail - id: MailJunkFood - suffix: junk food - components: - - type: Mail - contents: - - id: FoodBoxDonkpocket - - id: FoodSnackChips - -- type: entity - noSpawn: true - parent: BaseMail - id: MailKatana - suffix: Katana - components: - - type: Mail - contents: - - id: Katana - prob: 0.1 - orGroup: Katana - - id: KatanaDulled - prob: 0.9 - orGroup: Katana - -- type: entity - noSpawn: true - parent: BaseMail - id: MailKnife - suffix: Knife - components: - - type: Mail - contents: - - id: CombatKnife - -- type: entity - noSpawn: true - parent: BaseMail - id: MailMoney - suffix: money - components: - - type: Mail - contents: - - id: SpaceCash100 - orGroup: Cash - prob: 0.3 - - id: SpaceCash500 - orGroup: Cash - prob: 0.6 - - id: SpaceCash1000 - orGroup: Cash - prob: 0.3 - -- type: entity - noSpawn: true - parent: BaseMail - id: MailMuffins - suffix: muffins - components: - - type: Mail - isPriority: true - contents: - - id: FoodBakedMuffinBerry - - id: FoodBakedMuffinCherry - - id: FoodBakedMuffinBluecherry - -- type: entity - noSpawn: true - parent: BaseMail - id: MailMoffins - suffix: moffins - components: - - type: Mail - isPriority: true - contents: - - id: FoodMothMoffin - amount: 3 - -- type: entity - noSpawn: true - parent: BaseMail - id: MailNoir - suffix: noir - components: - - type: Mail - contents: - - id: ClothingUniformJumpsuitDetectiveGrey - - id: ClothingUniformJumpskirtDetectiveGrey - - id: ClothingHeadHatBowlerHat - - id: ClothingOuterCoatGentle - -- type: entity - noSpawn: true - parent: BaseMail - id: MailPAI - suffix: PAI - components: - - type: Mail - contents: - - id: PersonalAI - -- type: entity - noSpawn: true - parent: BaseMail - id: MailPlushie - suffix: plushie - components: - - type: Mail - contents: - - id: PlushieMothRandom - orGroup: Prize - - id: PlushieMothMusician - orGroup: Prize - - id: PlushieMothBartender - orGroup: Prize - - id: PlushieBee - orGroup: Prize - - id: PlushieHampter - orGroup: Prize - - id: PlushieRouny - orGroup: Prize - - id: PlushieLamp - orGroup: Prize - - id: PlushieArachind - orGroup: Prize - - id: PlushieLizard - orGroup: Prize - - id: PlushieLizardMirrored - orGroup: Prize - - id: PlushieSpaceLizard - orGroup: Prize - - id: PlushieDiona - orGroup: Prize - - id: PlushieSharkBlue - orGroup: Prize - - id: PlushieSharkPink - orGroup: Prize - - id: PlushieSharkGrey - orGroup: Prize - - id: PlushieCarp - orGroup: Prize - - id: PlushieMagicarp - orGroup: Prize - - id: PlushieHolocarp - orGroup: Prize - - id: PlushieSlime - orGroup: Prize - - id: PlushieSnake - orGroup: Prize - - id: PlushieVox - orGroup: Prize - - id: PlushieAtmosian - orGroup: Prize - - id: PlushiePenguin - orGroup: Prize - - id: PlushieHuman - orGroup: Prize - - id: PlushieArachne - orGroup: Prize - - id: PlushieGnome - orGroup: Prize - - id: PlushieLoveable - orGroup: Prize - - id: PlushieDeer - orGroup: Prize - - id: PlushieIpc - orGroup: Prize - - id: PlushieGrey - orGroup: Prize - - id: PlushieRedFox - orGroup: Prize - - id: PlushiePurpleFox - orGroup: Prize - - id: PlushiePinkFox - orGroup: Prize - - id: PlushieOrangeFox - orGroup: Prize - - id: PlushieMarbleFox - orGroup: Prize - - id: PlushieCrimsonFox - orGroup: Prize - - id: PlushieCoffeeFox - orGroup: Prize - - id: PlushieBlueFox - orGroup: Prize - - id: PlushieBlackFox - orGroup: Prize - - id: PlushieVulp - orGroup: Prize - - id: PlushieCorgi - orGroup: Prize - - id: PlushieGirlyCorgi - orGroup: Prize - - id: PlushieRobotCorgi - orGroup: Prize - - id: PlushieCatBlack - orGroup: Prize - - id: PlushieCatGrey - orGroup: Prize - - id: PlushieCatOrange - orGroup: Prize - - id: PlushieCatSiames - orGroup: Prize - - id: PlushieCatTabby - orGroup: Prize - - id: PlushieCatTuxedo - orGroup: Prize - - id: PlushieCatWhite - orGroup: Prize - - id: PlushieGhost - orGroup: Prize - - id: PlushieRGBee - orGroup: Prize - - id: PlushieRatvar - orGroup: Prize - - id: PlushieNar - orGroup: Prize - - id: PlushieRainbowCarp - orGroup: Prize - - id: PlushieXeno - orGroup: Prize - - id: PlushieJester - orGroup: Prize - - id: PlushieSlips - orGroup: Prize - - id: PlushieAbductor - orGroup: Prize - - id: PlushieTrystan - orGroup: Prize - - id: PlushieAbductorAgent - orGroup: Prize - - id: PlushieNuke - orGroup: Prize - -- type: entity - noSpawn: true - parent: BaseMail - id: MailRestraints - suffix: restraints - components: - - type: Mail - contents: - - id: Handcuffs - - id: ClothingMaskMuzzle - - id: ClothingEyesBlindfold - -- type: entity - noSpawn: true - parent: BaseMail - id: MailSignallerKit - suffix: signallerkit - components: - - type: Mail - contents: - - id: Multitool - - id: RemoteSignaller - -# - type: entity -# noSpawn: true -# parent: BaseMail -# id: MailSixPack -# suffix: sixpack -# components: -# - type: Mail -# contents: -# - id: DrinkCanPack - -- type: entity - noSpawn: true - parent: BaseMail - id: MailSkub - suffix: skub - components: - - type: Mail - contents: - - id: Skub - -- type: entity - noSpawn: true - parent: BaseMail - id: MailSoda - suffix: soda - components: - - type: Mail - contents: - - id: DrinkColaBottleFull - orGroup: Soda - - id: DrinkSpaceMountainWindBottleFull - orGroup: Soda - - id: DrinkSpaceUpBottleFull - orGroup: Soda - -- type: entity - noSpawn: true - parent: BaseMail - id: MailSpaceVillainDIY - suffix: spacevilliandiy - components: - - type: Mail - contents: - - id: SpaceVillainArcadeComputerCircuitboard - -- type: entity - noSpawn: true - parent: BaseMail - id: MailSunglasses - suffix: Sunglasses - components: - - type: Mail - contents: - - id: ClothingEyesGlassesSunglasses - -- type: entity - noSpawn: true - parent: BaseMail - id: MailVagueThreat - suffix: vague-threat - components: - - type: Mail - contents: - - id: PaperMailVagueThreat1 - orGroup: Paper - - id: PaperMailVagueThreat2 - orGroup: Paper - - id: PaperMailVagueThreat3 - orGroup: Paper - - id: PaperMailVagueThreat4 - orGroup: Paper - - id: PaperMailVagueThreat5 - orGroup: Paper - - id: PaperMailVagueThreat6 - orGroup: Paper - - id: PaperMailVagueThreat7 - orGroup: Paper - - id: PaperMailVagueThreat8 - orGroup: Paper - - id: PaperMailVagueThreat9 - orGroup: Paper - - id: PaperMailVagueThreat10 - orGroup: Paper - - id: PaperMailVagueThreat11 - orGroup: Paper - - id: PaperMailVagueThreat12 - orGroup: Paper - - id: KitchenKnife - orGroup: ThreateningObject - - id: ButchCleaver - orGroup: ThreateningObject - - id: CombatKnife - orGroup: ThreateningObject - - id: SurvivalKnife - orGroup: ThreateningObject - - id: SoapHomemade - orGroup: ThreateningObject - - id: FoodMeat - orGroup: ThreateningObject - - id: OrganHumanHeart - orGroup: ThreateningObject - -- type: entity - noSpawn: true - parent: BaseMail - id: MailWinterCoat - suffix: wintercoat - components: - - type: Mail - contents: - - id: ClothingOuterWinterCoat - orGroup: Coat - - id: ClothingOuterWinterCoatLong - orGroup: Coat - - id: ClothingOuterWinterCoatPlaid - orGroup: Coat diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Mail/mail_command.yml b/Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Mail/mail_command.yml deleted file mode 100644 index 7e2a935f90..0000000000 --- a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Mail/mail_command.yml +++ /dev/null @@ -1,9 +0,0 @@ -- type: entity - noSpawn: true - parent: BaseMail - id: MailCommandPinpointerNuclear - suffix: pinpointernuclear - components: - - type: Mail - contents: - - id: PinpointerNuclear diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Mail/mail_engineering.yml b/Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Mail/mail_engineering.yml deleted file mode 100644 index 461d9bf136..0000000000 --- a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Mail/mail_engineering.yml +++ /dev/null @@ -1,45 +0,0 @@ -- type: entity - noSpawn: true - parent: BaseMail - id: MailEngineeringCables - suffix: cables - components: - - type: Mail - contents: - - id: CableHVStack - orGroup: Cables - - id: CableMVStack - orGroup: Cables - - id: CableApcStack - orGroup: Cables - -- type: entity - noSpawn: true - parent: BaseMail - id: MailEngineeringKudzuDeterrent - suffix: antikudzu - components: - - type: Mail - contents: - - id: PlantBGoneSpray - -- type: entity - noSpawn: true - parent: BaseMail - id: MailEngineeringSheetGlass - suffix: sheetglass - components: - - type: Mail - contents: - - id: SheetGlass - -- type: entity - noSpawn: true - parent: BaseMail - id: MailEngineeringWelderReplacement - suffix: welder - components: - - type: Mail - contents: - - id: Welder - diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Mail/mail_specific_items.yml b/Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Mail/mail_specific_items.yml deleted file mode 100644 index b4d2b54779..0000000000 --- a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Mail/mail_specific_items.yml +++ /dev/null @@ -1,169 +0,0 @@ -- type: entity - id: PaperMailCallForHelp1 - noSpawn: true - suffix: "call for help 1" - parent: Paper - components: - - type: Paper - content: | - Help! They're coming! Take this! - -- type: entity - id: PaperMailCallForHelp2 - noSpawn: true - suffix: "call for help 2" - parent: Paper - components: - - type: Paper - content: | - Check disposals! - -- type: entity - id: PaperMailCallForHelp3 - noSpawn: true - suffix: "call for help 3" - parent: Paper - components: - - type: Paper - content: | - GET ME OUT! - -- type: entity - id: PaperMailCallForHelp4 - noSpawn: true - suffix: "call for help 4" - parent: Paper - components: - - type: Paper - content: | - Check maintenance! - -- type: entity - id: PaperMailCallForHelp5 - noSpawn: true - suffix: "call for help 5" - parent: Paper - components: - - type: Paper - content: | - Save me, please! - -- type: entity - id: PaperMailVagueThreat1 - noSpawn: true - suffix: "vague mail threat 1" - parent: Paper - components: - - type: Paper - content: | - I know what you did. You don't know what I'm going to do to you. - -- type: entity - id: PaperMailVagueThreat2 - noSpawn: true - suffix: "vague mail threat 2" - parent: Paper - components: - - type: Paper - content: | - I'm coming for you. - -- type: entity - id: PaperMailVagueThreat3 - noSpawn: true - suffix: "vague mail threat 3" - parent: Paper - components: - - type: Paper - content: | - You're next. - -- type: entity - id: PaperMailVagueThreat4 - noSpawn: true - suffix: "vague mail threat 4" - parent: Paper - components: - - type: Paper - content: | - We see you. - -- type: entity - id: PaperMailVagueThreat5 - noSpawn: true - suffix: "vague mail threat 5" - parent: Paper - components: - - type: Paper - content: | - I hope your affairs are in order. - -- type: entity - id: PaperMailVagueThreat6 - noSpawn: true - suffix: "vague mail threat 6" - parent: Paper - components: - - type: Paper - content: | - It's only a matter of time. Enjoy it while it lasts. - -- type: entity - id: PaperMailVagueThreat7 - noSpawn: true - suffix: "vague mail threat 7" - parent: Paper - components: - - type: Paper - content: | - Who should we mail your pieces to? - -- type: entity - id: PaperMailVagueThreat8 - noSpawn: true - suffix: "vague mail threat 8" - parent: Paper - components: - - type: Paper - content: | - Do you prefer to die slowly or quickly? Just kidding. We don't care what you think. - -- type: entity - id: PaperMailVagueThreat9 - noSpawn: true - suffix: "vague mail threat 9" - parent: Paper - components: - - type: Paper - content: | - I think your head would look nice on my mantel. - -- type: entity - id: PaperMailVagueThreat10 - noSpawn: true - suffix: "vague mail threat 10" - parent: Paper - components: - - type: Paper - content: | - You should have paid up. It's too late now. - -- type: entity - id: PaperMailVagueThreat11 - noSpawn: true - suffix: "vague mail threat 11" - parent: Paper - components: - - type: Paper - content: | - Your family will miss you, but don't worry. We'll take care of them too. - -- type: entity - id: PaperMailVagueThreat12 - noSpawn: true - suffix: "vague mail threat 12" - parent: Paper - components: - - type: Paper - content: | - I have a bet that you're going to die today. I'm not afraid of cheating. diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Stations/mail.yml b/Resources/Prototypes/Nyanotrasen/Entities/Stations/mail.yml index ceb87bbaa1..b85cfef87a 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Stations/mail.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Stations/mail.yml @@ -3,3 +3,4 @@ abstract: true components: - type: StationMailRouter + - type: StationLogisticStats # DeltaV - Tracks statistics related to mail and income diff --git a/Resources/Prototypes/Recipes/Lathes/misc.yml b/Resources/Prototypes/Recipes/Lathes/misc.yml index ab13dc4573..2c0e1eeec3 100644 --- a/Resources/Prototypes/Recipes/Lathes/misc.yml +++ b/Resources/Prototypes/Recipes/Lathes/misc.yml @@ -207,3 +207,19 @@ materials: Steel: 400 Glass: 200 + +- type: latheRecipe + id: ClothingShoesBootsMagAdv + result: ClothingShoesBootsMagAdv + completetime: 12 + materials: + Silver: 1000 + Gold: 500 + +- type: latheRecipe + id: MailCapsule + result: MailCapsulePrimed + completetime: 1 + materials: + Glass: 100 + Plastic: 100 diff --git a/Resources/Prototypes/Traits/physical.yml b/Resources/Prototypes/Traits/physical.yml index 8debf7ffbf..ec8760a095 100644 --- a/Resources/Prototypes/Traits/physical.yml +++ b/Resources/Prototypes/Traits/physical.yml @@ -356,6 +356,10 @@ category: Physical points: -4 requirements: + - !type:CharacterJobRequirement + inverted: true + jobs: + - Prisoner # Bionics should be "Confiscated" from long term prisoners. - !type:CharacterSpeciesRequirement species: - Human # Entirely arbitrary, I've decided I want a trait unique to humans. Since they don't normally get anything exciting. @@ -390,6 +394,10 @@ category: Physical points: -4 requirements: + - !type:CharacterJobRequirement + inverted: true + jobs: + - Prisoner # Bionics should be "Confiscated" from long term prisoners. - !type:CharacterSpeciesRequirement inverted: true species: @@ -401,3 +409,183 @@ productionLength: 2 entityProduced: MaterialWebSilk1 hungerCost: 4 + +- type: trait + id: BionicArm + category: Physical + points: -9 + requirements: + - !type:CharacterJobRequirement + inverted: true + jobs: + - Prisoner # Bionics should be "Confiscated" from long term prisoners. + components: + - type: Prying + speedModifier: 1 + pryPowered: true + force: true + +- type: trait + id: PlateletFactories + category: Physical + points: -10 + requirements: + - !type:CharacterJobRequirement + inverted: true + jobs: + - Prisoner # Bionics should be "Confiscated" from long term prisoners. + - !type:CharacterSpeciesRequirement + inverted: true + species: + - IPC + componentRemovals: + - PassiveDamage + components: + - type: PassiveDamage + allowedStates: + - Alive + - Critical + damageCap: 200 + damage: + groups: + Brute: -0.07 + Burn: -0.07 + Airloss: -0.07 + Toxin: -0.07 + Genetic: -0.07 + +- type: trait + id: DermalArmor + category: Physical + points: -9 + requirements: + - !type:CharacterJobRequirement + inverted: true + jobs: + - Prisoner # Bionics should be "Confiscated" from long term prisoners. + - !type:CharacterSpeciesRequirement + species: + - Human + componentRemovals: + - Damageable + components: + - type: Damageable + damageModifierSet: DermalArmor + +- type: trait + id: CyberEyes + category: Physical + points: -8 + requirements: + - !type:CharacterJobRequirement + inverted: true + jobs: + - Prisoner # Bionics should be "Confiscated" from long term prisoners. + - !type:CharacterTraitRequirement + inverted: true + traits: + - Photophobia + - Blindness + - Nearsighted + componentRemovals: + - Flashable + components: + - type: FlashImmunity + - type: EyeProtection + - type: CyberEyes + +- type: trait + id: CyberEyesSecurity + category: Physical + points: -1 + requirements: + - !type:CharacterJobRequirement + inverted: true + jobs: + - Prisoner # Bionics should be "Confiscated" from long term prisoners. + - !type:CharacterDepartmentRequirement + departments: + - Security + - !type:CharacterTraitRequirement + traits: + - CyberEyes + - !type:CharacterTraitRequirement + inverted: true + traits: + - CyberEyesOmni + components: + - type: ShowSecurityIcons + +- type: trait + id: CyberEyesMedical + category: Physical + points: -1 + requirements: + - !type:CharacterJobRequirement + inverted: true + jobs: + - Prisoner # Bionics should be "Confiscated" from long term prisoners. + - !type:CharacterTraitRequirement + traits: + - CyberEyes + - !type:CharacterTraitRequirement + inverted: true + traits: + - CyberEyesDiagnostic + - CyberEyesOmni + components: + - type: ShowHealthBars + damageContainers: + - Biological + +- type: trait + id: CyberEyesDiagnostic + category: Physical + points: -1 + requirements: + - !type:CharacterJobRequirement + inverted: true + jobs: + - Prisoner # Bionics should be "Confiscated" from long term prisoners. + - !type:CharacterTraitRequirement + traits: + - CyberEyes + - !type:CharacterTraitRequirement + inverted: true + traits: + - CyberEyesMedical + - CyberEyesOmni + components: + - type: ShowHealthBars + damageContainers: + - Inorganic + - Silicon + +- type: trait + id: CyberEyesOmni + category: Physical + points: -3 + requirements: + - !type:CharacterJobRequirement + inverted: true + jobs: + - Prisoner # Bionics should be "Confiscated" from long term prisoners. + - !type:CharacterDepartmentRequirement + departments: + - Security + - !type:CharacterTraitRequirement + traits: + - CyberEyes + - !type:CharacterTraitRequirement + inverted: true + traits: + - CyberEyesMedical + - CyberEyesDiagnostic + - CyberEyesSecurity + components: + - type: ShowSecurityIcons + - type: ShowHealthBars + damageContainers: + - Biological + - Inorganic + - Silicon diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index cc81a2ed64..77cc4f372f 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -25,6 +25,9 @@ - type: Tag id: Arrow +- type: Tag + id: Ash + - type: Tag id: ATVKeys @@ -352,6 +355,9 @@ - type: Tag id: CartridgeRocket +- type: Tag + id: CaveFactory + # Allows you to walk over tile entities such as lava without steptrigger - type: Tag id: Catwalk @@ -803,6 +809,12 @@ - type: Tag id: MacroBomb +- type: Tag + id: Mail + +- type: Tag + id: MailCapsule + - type: Tag id: MimeBelt @@ -916,6 +928,12 @@ - type: Tag id: Multitool +- type: Tag + id: Mustard + +- type: Tag + id: MysteryFigureBox + - type: Tag id: NoBlockAnchoring @@ -1295,6 +1313,9 @@ - type: Tag id: WhitelistChameleon +- type: Tag + id: WhoopieCushion + - type: Tag id: Window diff --git a/Resources/Textures/DeltaV/Objects/Devices/cartridge.rsi/cart-mail.png b/Resources/Textures/DeltaV/Objects/Devices/cartridge.rsi/cart-mail.png new file mode 100644 index 0000000000..5734abb6fd Binary files /dev/null and b/Resources/Textures/DeltaV/Objects/Devices/cartridge.rsi/cart-mail.png differ diff --git a/Resources/Textures/DeltaV/Objects/Devices/cartridge.rsi/meta.json b/Resources/Textures/DeltaV/Objects/Devices/cartridge.rsi/meta.json index 293870d3a3..4a4ba3352f 100644 --- a/Resources/Textures/DeltaV/Objects/Devices/cartridge.rsi/meta.json +++ b/Resources/Textures/DeltaV/Objects/Devices/cartridge.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Timfa", + "copyright": "Timfa, plus edits by portfiend", "size": { "x": 32, "y": 32 @@ -9,6 +9,9 @@ "states": [ { "name": "cart-cri" + }, + { + "name": "cart-mail" } ] -} \ No newline at end of file +} diff --git a/Resources/Textures/Objects/Misc/mail_capsule.rsi/icon-cash.png b/Resources/Textures/Objects/Misc/mail_capsule.rsi/icon-cash.png new file mode 100644 index 0000000000..8ba33c95b5 Binary files /dev/null and b/Resources/Textures/Objects/Misc/mail_capsule.rsi/icon-cash.png differ diff --git a/Resources/Textures/Objects/Misc/mail_capsule.rsi/icon-empty.png b/Resources/Textures/Objects/Misc/mail_capsule.rsi/icon-empty.png new file mode 100644 index 0000000000..085b787410 Binary files /dev/null and b/Resources/Textures/Objects/Misc/mail_capsule.rsi/icon-empty.png differ diff --git a/Resources/Textures/Objects/Misc/mail_capsule.rsi/icon-food.png b/Resources/Textures/Objects/Misc/mail_capsule.rsi/icon-food.png new file mode 100644 index 0000000000..d08489cec8 Binary files /dev/null and b/Resources/Textures/Objects/Misc/mail_capsule.rsi/icon-food.png differ diff --git a/Resources/Textures/Objects/Misc/mail_capsule.rsi/icon-mail.png b/Resources/Textures/Objects/Misc/mail_capsule.rsi/icon-mail.png new file mode 100644 index 0000000000..b35a2acc0f Binary files /dev/null and b/Resources/Textures/Objects/Misc/mail_capsule.rsi/icon-mail.png differ diff --git a/Resources/Textures/Objects/Misc/mail_capsule.rsi/meta.json b/Resources/Textures/Objects/Misc/mail_capsule.rsi/meta.json new file mode 100644 index 0000000000..2e9bfc3617 --- /dev/null +++ b/Resources/Textures/Objects/Misc/mail_capsule.rsi/meta.json @@ -0,0 +1,26 @@ +{ + "version": 1, + "license": "CC-BY-SA-4.0", + "copyright": "Made for Frontier by erhardsteinhauer (discord)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon-empty" + }, + { + "name": "icon-mail" + }, + { + "name": "icon-food" + }, + { + "name": "icon-cash" + }, + { + "name": "spent" + } + ] +} diff --git a/Resources/Textures/Objects/Misc/mail_capsule.rsi/spent.png b/Resources/Textures/Objects/Misc/mail_capsule.rsi/spent.png new file mode 100644 index 0000000000..acd0d0577f Binary files /dev/null and b/Resources/Textures/Objects/Misc/mail_capsule.rsi/spent.png differ diff --git a/Resources/Textures/Nyanotrasen/Objects/Specific/Mail/mail.rsi/broken.png b/Resources/Textures/Objects/Specific/Mail/mail.rsi/broken.png similarity index 100% rename from Resources/Textures/Nyanotrasen/Objects/Specific/Mail/mail.rsi/broken.png rename to Resources/Textures/Objects/Specific/Mail/mail.rsi/broken.png diff --git a/Resources/Textures/Nyanotrasen/Objects/Specific/Mail/mail.rsi/fragile.png b/Resources/Textures/Objects/Specific/Mail/mail.rsi/fragile.png similarity index 100% rename from Resources/Textures/Nyanotrasen/Objects/Specific/Mail/mail.rsi/fragile.png rename to Resources/Textures/Objects/Specific/Mail/mail.rsi/fragile.png diff --git a/Resources/Textures/Nyanotrasen/Objects/Specific/Mail/mail.rsi/icon.png b/Resources/Textures/Objects/Specific/Mail/mail.rsi/icon.png similarity index 100% rename from Resources/Textures/Nyanotrasen/Objects/Specific/Mail/mail.rsi/icon.png rename to Resources/Textures/Objects/Specific/Mail/mail.rsi/icon.png diff --git a/Resources/Textures/Nyanotrasen/Objects/Specific/Mail/mail.rsi/locked.png b/Resources/Textures/Objects/Specific/Mail/mail.rsi/locked.png similarity index 100% rename from Resources/Textures/Nyanotrasen/Objects/Specific/Mail/mail.rsi/locked.png rename to Resources/Textures/Objects/Specific/Mail/mail.rsi/locked.png diff --git a/Resources/Textures/Nyanotrasen/Objects/Specific/Mail/mail.rsi/meta.json b/Resources/Textures/Objects/Specific/Mail/mail.rsi/meta.json similarity index 100% rename from Resources/Textures/Nyanotrasen/Objects/Specific/Mail/mail.rsi/meta.json rename to Resources/Textures/Objects/Specific/Mail/mail.rsi/meta.json diff --git a/Resources/Textures/Nyanotrasen/Objects/Specific/Mail/mail.rsi/postmark.png b/Resources/Textures/Objects/Specific/Mail/mail.rsi/postmark.png similarity index 100% rename from Resources/Textures/Nyanotrasen/Objects/Specific/Mail/mail.rsi/postmark.png rename to Resources/Textures/Objects/Specific/Mail/mail.rsi/postmark.png diff --git a/Resources/Textures/Nyanotrasen/Objects/Specific/Mail/mail.rsi/priority.png b/Resources/Textures/Objects/Specific/Mail/mail.rsi/priority.png similarity index 100% rename from Resources/Textures/Nyanotrasen/Objects/Specific/Mail/mail.rsi/priority.png rename to Resources/Textures/Objects/Specific/Mail/mail.rsi/priority.png diff --git a/Resources/Textures/Nyanotrasen/Objects/Specific/Mail/mail.rsi/priority_inactive.png b/Resources/Textures/Objects/Specific/Mail/mail.rsi/priority_inactive.png similarity index 100% rename from Resources/Textures/Nyanotrasen/Objects/Specific/Mail/mail.rsi/priority_inactive.png rename to Resources/Textures/Objects/Specific/Mail/mail.rsi/priority_inactive.png diff --git a/Resources/Textures/Nyanotrasen/Objects/Specific/Mail/mail.rsi/trash.png b/Resources/Textures/Objects/Specific/Mail/mail.rsi/trash.png similarity index 100% rename from Resources/Textures/Nyanotrasen/Objects/Specific/Mail/mail.rsi/trash.png rename to Resources/Textures/Objects/Specific/Mail/mail.rsi/trash.png diff --git a/Resources/Textures/Objects/Specific/Mail/mail_large.rsi/broken.png b/Resources/Textures/Objects/Specific/Mail/mail_large.rsi/broken.png new file mode 100644 index 0000000000..1c798c4075 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Mail/mail_large.rsi/broken.png differ diff --git a/Resources/Textures/Objects/Specific/Mail/mail_large.rsi/fragile.png b/Resources/Textures/Objects/Specific/Mail/mail_large.rsi/fragile.png new file mode 100644 index 0000000000..0917000cbe Binary files /dev/null and b/Resources/Textures/Objects/Specific/Mail/mail_large.rsi/fragile.png differ diff --git a/Resources/Textures/Objects/Specific/Mail/mail_large.rsi/icon.png b/Resources/Textures/Objects/Specific/Mail/mail_large.rsi/icon.png new file mode 100644 index 0000000000..f3974ab116 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Mail/mail_large.rsi/icon.png differ diff --git a/Resources/Textures/Objects/Specific/Mail/mail_large.rsi/inhand-left.png b/Resources/Textures/Objects/Specific/Mail/mail_large.rsi/inhand-left.png new file mode 100644 index 0000000000..ccbc87cf3b Binary files /dev/null and b/Resources/Textures/Objects/Specific/Mail/mail_large.rsi/inhand-left.png differ diff --git a/Resources/Textures/Objects/Specific/Mail/mail_large.rsi/inhand-right.png b/Resources/Textures/Objects/Specific/Mail/mail_large.rsi/inhand-right.png new file mode 100644 index 0000000000..ccbc87cf3b Binary files /dev/null and b/Resources/Textures/Objects/Specific/Mail/mail_large.rsi/inhand-right.png differ diff --git a/Resources/Textures/Objects/Specific/Mail/mail_large.rsi/locked.png b/Resources/Textures/Objects/Specific/Mail/mail_large.rsi/locked.png new file mode 100644 index 0000000000..1cacaf1921 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Mail/mail_large.rsi/locked.png differ diff --git a/Resources/Textures/Objects/Specific/Mail/mail_large.rsi/meta.json b/Resources/Textures/Objects/Specific/Mail/mail_large.rsi/meta.json new file mode 100644 index 0000000000..ac5345ba1a --- /dev/null +++ b/Resources/Textures/Objects/Specific/Mail/mail_large.rsi/meta.json @@ -0,0 +1,40 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation (obj/storage/closet.dmi, obj/service/bureaucracy.dmi), modified by Whatstone (Discord). broken, inhand-left, inhand-right by Whatstone.", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "broken" + }, + { + "name": "fragile" + }, + { + "name": "icon" + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "locked" + }, + { + "name": "priority" + }, + { + "name": "priority_inactive" + }, + { + "name": "trash" + } + ] +} diff --git a/Resources/Textures/Objects/Specific/Mail/mail_large.rsi/priority.png b/Resources/Textures/Objects/Specific/Mail/mail_large.rsi/priority.png new file mode 100644 index 0000000000..9c5a74ad10 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Mail/mail_large.rsi/priority.png differ diff --git a/Resources/Textures/Objects/Specific/Mail/mail_large.rsi/priority_inactive.png b/Resources/Textures/Objects/Specific/Mail/mail_large.rsi/priority_inactive.png new file mode 100644 index 0000000000..fc03165b57 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Mail/mail_large.rsi/priority_inactive.png differ diff --git a/Resources/Textures/Objects/Specific/Mail/mail_large.rsi/trash.png b/Resources/Textures/Objects/Specific/Mail/mail_large.rsi/trash.png new file mode 100644 index 0000000000..2ef4ee7233 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Mail/mail_large.rsi/trash.png differ diff --git a/Resources/Textures/Objects/Weapons/Guns/Launchers/mail.rsi/bolt-open.png b/Resources/Textures/Objects/Weapons/Guns/Launchers/mail.rsi/bolt-open.png new file mode 100644 index 0000000000..87c6d812ec Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Launchers/mail.rsi/bolt-open.png differ diff --git a/Resources/Textures/Objects/Weapons/Guns/Launchers/mail.rsi/equipped-BACKPACK.png b/Resources/Textures/Objects/Weapons/Guns/Launchers/mail.rsi/equipped-BACKPACK.png new file mode 100644 index 0000000000..c17a68eb35 Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Launchers/mail.rsi/equipped-BACKPACK.png differ diff --git a/Resources/Textures/Objects/Weapons/Guns/Launchers/mail.rsi/equipped-BELT.png b/Resources/Textures/Objects/Weapons/Guns/Launchers/mail.rsi/equipped-BELT.png new file mode 100644 index 0000000000..59dc5f13ed Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Launchers/mail.rsi/equipped-BELT.png differ diff --git a/Resources/Textures/Objects/Weapons/Guns/Launchers/mail.rsi/icon.png b/Resources/Textures/Objects/Weapons/Guns/Launchers/mail.rsi/icon.png new file mode 100644 index 0000000000..dd7b21e167 Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Launchers/mail.rsi/icon.png differ diff --git a/Resources/Textures/Objects/Weapons/Guns/Launchers/mail.rsi/inhand-left.png b/Resources/Textures/Objects/Weapons/Guns/Launchers/mail.rsi/inhand-left.png new file mode 100644 index 0000000000..427389f40d Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Launchers/mail.rsi/inhand-left.png differ diff --git a/Resources/Textures/Objects/Weapons/Guns/Launchers/mail.rsi/inhand-right.png b/Resources/Textures/Objects/Weapons/Guns/Launchers/mail.rsi/inhand-right.png new file mode 100644 index 0000000000..8e50ef04f9 Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Launchers/mail.rsi/inhand-right.png differ diff --git a/Resources/Textures/Objects/Weapons/Guns/Launchers/mail.rsi/meta.json b/Resources/Textures/Objects/Weapons/Guns/Launchers/mail.rsi/meta.json new file mode 100644 index 0000000000..464c22d1a7 --- /dev/null +++ b/Resources/Textures/Objects/Weapons/Guns/Launchers/mail.rsi/meta.json @@ -0,0 +1,33 @@ +{ + "version": 1, + "license": "CC-BY-SA-4.0", + "copyright": "Taken/modified from cev-eris at https://github.com/discordia-space/CEV-Eris/pull/6042/commits/64916c98f4847acc4adf3a2416bf78c005fd7dd7, https://github.com/discordia-space/CEV-Eris/blob/master/icons/obj/guns/launcher/grenadelauncher.dmi, backpack sprite by Peptide, resprited for mail gun by erhardsteinhauer (discord)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "bolt-open" + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "equipped-BELT", + "directions": 4 + }, + { + "name": "equipped-BACKPACK", + "directions": 4 + } + ] +} \ No newline at end of file diff --git a/Resources/keybinds.yml b/Resources/keybinds.yml index 33b4166161..fdc872887e 100644 --- a/Resources/keybinds.yml +++ b/Resources/keybinds.yml @@ -264,6 +264,10 @@ binds: - function: ToggleStanding type: State key: R +- function: ToggleCrawlingUnder + type: State + mod1: Shift + key: R - function: ShowDebugConsole type: State key: Tilde