From 60dd693ea2c615b1b3bf14a1f294d6da9c452cbf Mon Sep 17 00:00:00 2001 From: Fansana <116083121+Fansana@users.noreply.github.com> Date: Sun, 13 Oct 2024 18:38:30 +0200 Subject: [PATCH 01/19] Fix Arrivals Shuttle (#1040) # Description Fixes the arrivals shuttle choosing to correct docking port. --- - [ ] Fix all maps and place the correct airlock (yeah not gonna do that in this PR) - [x] Fixed arrivals shuttle logic --- # Changelog :cl: - fix: Fixes the arrivals shuttle choosing to correct docking port. --- Content.Server/Shuttles/Systems/ArrivalsSystem.cs | 4 ++-- Content.Shared/CCVar/CCVars.cs | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) 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.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index 7714122d9f..0d85d089f1 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. /// From f29a5bf74645b491a5be8095c192c6e582ab81c6 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Sun, 13 Oct 2024 16:38:59 +0000 Subject: [PATCH 02/19] Automatic Changelog Update (#1040) --- Resources/Changelog/Changelog.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 0f1bf764b8..89d11ae503 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7165,3 +7165,10 @@ 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 From 124af1200ddea9659fa79d02eda75dc5231ec041 Mon Sep 17 00:00:00 2001 From: FoxxoTrystan <45297731+FoxxoTrystan@users.noreply.github.com> Date: Sun, 13 Oct 2024 20:33:34 +0200 Subject: [PATCH 03/19] Custom Name Fixes (#1032) # Description This is an emercency fix regarding Custom Name, apprently this is fucked... wow... Regarding DB issues please @DEATHB4DEFEAT review or check if you can assist or anyone. Thank you. --- # Changelog :cl: - fix: Custom Specie Name! --------- Signed-off-by: FoxxoTrystan <45297731+FoxxoTrystan@users.noreply.github.com> Co-authored-by: VMSolidus Co-authored-by: DEATHB4DEFEAT <77995199+DEATHB4DEFEAT@users.noreply.github.com> --- .../Postgres/20241001054803_CustomSpecieName.cs | 17 +++++------------ .../Sqlite/20241001054735_CustomSpecieName.cs | 15 ++++----------- .../Preferences/HumanoidCharacterProfile.cs | 10 +++++++--- 3 files changed, 16 insertions(+), 26 deletions(-) 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.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) From d84a728fd65b09ca7beed8b8d8eaf8cc5e3ccce5 Mon Sep 17 00:00:00 2001 From: Remuchi <72476615+Remuchi@users.noreply.github.com> Date: Mon, 14 Oct 2024 01:33:43 +0700 Subject: [PATCH 04/19] Cherry Pick "Fix Latejoin Antag Preferences Not Being Respected" (#1038) # Description meow. Project compiles and starts. Is the bug gone? I hope so. --- # Changelog :cl: - fix: Latejoin antag selection should respect player's antag preferences. --------- Co-authored-by: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> --- .../Antag/AntagSelectionSystem.API.cs | 31 +++++++++++++++++ Content.Server/Antag/AntagSelectionSystem.cs | 34 +++++++++---------- 2 files changed, 48 insertions(+), 17 deletions(-) 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; } From f1dd6796298eb22441182d801e891d758b9a6c9a Mon Sep 17 00:00:00 2001 From: FoxxoTrystan <45297731+FoxxoTrystan@users.noreply.github.com> Date: Sun, 13 Oct 2024 20:33:59 +0200 Subject: [PATCH 05/19] Add ArachneWeb to Spiderweb. (#1033) # Description This add the missing tags for webs for Oneirophage to hide in webs (works tested on floof) Overall Oneirophage need more changes, languages, psionic, ect but so far this is needed. --- # Changelog :cl: - fix: Oneirophage is once more psionic invisible in webs. Signed-off-by: FoxxoTrystan <45297731+FoxxoTrystan@users.noreply.github.com> --- Resources/Prototypes/Entities/Objects/Misc/spider_web.yml | 3 +++ 1 file changed, 3 insertions(+) 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 From 74fa664063935d473d0e8ea0f3777203a6187b2a Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Sun, 13 Oct 2024 18:34:39 +0000 Subject: [PATCH 06/19] Automatic Changelog Update (#1033) --- Resources/Changelog/Changelog.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 89d11ae503..773fa099c7 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7172,3 +7172,10 @@ Entries: 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 From aa799f5cc04f5b5c023c76f549568db438e558ce Mon Sep 17 00:00:00 2001 From: Mnemotechnican <69920617+Mnemotechnician@users.noreply.github.com> Date: Sun, 13 Oct 2024 21:34:58 +0300 Subject: [PATCH 07/19] Feat: Togglable Under-Table Crawling (#1036) # Description This reverts most code changes done by https://github.com/Simple-Station/Einstein-Engines/pull/939 and re-implements them in a better way: - Players can now toggle under-furniture crawling with a keybind (shift-R by default) - Crawling that way is 50% slower for obvious balancing reasons - The respective cvar for it is now true by default and prevents players from beginning the "crawl under furniture" thing Also cleaned up a few methods I was seriously pissed off by. There is still a lot to clean up and fix, but I will leave it for a dedicated PR in the future. # Why (balancing) Let me lie on the bed instead of under it!!!!!!!

Media

https://github.com/user-attachments/assets/5f04c82a-b88b-4005-8052-a1a6f011bcc9

# Changelog :cl: - add: You can now toggle crawling under furniture! The default keybind is Shift-R, you can change it in settings. --- Content.Client/Input/ContentContexts.cs | 1 + .../Options/UI/Tabs/KeyRebindTab.xaml.cs | 3 +- Content.Client/Standing/LayingDownSystem.cs | 43 +++++++--------- Content.Server/Standing/LayingDownSystem.cs | 5 ++ .../Assorted/LayingDownModifierSystem.cs | 2 +- Content.Shared/CCVar/CCVars.cs | 5 +- Content.Shared/Input/ContentKeyFunctions.cs | 1 + .../Standing/LayingDownComponent.cs | 26 ++++------ .../Standing/SharedLayingDownSystem.cs | 50 +++++++++++++++---- .../Standing/StandingStateSystem.cs | 14 +----- .../en-US/escape-menu/ui/options-menu.ftl | 1 + Resources/Locale/en-US/movement/laying.ftl | 3 ++ Resources/keybinds.yml | 4 ++ 13 files changed, 89 insertions(+), 69 deletions(-) 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/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 0d85d089f1..b8c37baa1a 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -2487,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/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/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/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/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/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 From d3e4d1453e95c804fa23b3b8fb22f2f7b865a815 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Sun, 13 Oct 2024 18:36:51 +0000 Subject: [PATCH 08/19] Automatic Changelog Update (#1036) --- Resources/Changelog/Changelog.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 773fa099c7..a6fa8e01bc 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7179,3 +7179,12 @@ Entries: 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 From fcada7494413cbe881cda6db23f9de40c42e79b2 Mon Sep 17 00:00:00 2001 From: DEATHB4DEFEAT <77995199+DEATHB4DEFEAT@users.noreply.github.com> Date: Sun, 13 Oct 2024 11:41:15 -0700 Subject: [PATCH 09/19] Improve "Show Clothing" Button Labels (#1030) They also didn't have `:`s like everything else. # Changelog :cl: - tweak: Made the show clothing/loadouts button labels more clear Signed-off-by: DEATHB4DEFEAT <77995199+DEATHB4DEFEAT@users.noreply.github.com> --- .../Locale/en-US/preferences/ui/humanoid-profile-editor.ftl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 From 053d42bc21e7c04c39a54862454201b08322cb88 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Sun, 13 Oct 2024 18:41:40 +0000 Subject: [PATCH 10/19] Automatic Changelog Update (#1030) --- Resources/Changelog/Changelog.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index a6fa8e01bc..fe8dd2b2cd 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7188,3 +7188,10 @@ Entries: 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 From 8fec66690df27612a2240d5ac016864bdcb1fa4f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 13 Oct 2024 11:50:13 -0700 Subject: [PATCH 11/19] Update Credits (#1043) This is an automated Pull Request. This PR updates the GitHub contributors in the credits section. Co-authored-by: SimpleStation Changelogs --- Resources/Credits/GitHub.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From c377ee5fef25a940cc3cf7a680a4b13d6951efcb Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Sun, 13 Oct 2024 14:51:26 -0400 Subject: [PATCH 12/19] Make Brains Not Food (#1044) # Description This is a fucking stupid method of irreversible round removal. Gib someone and then eat their brain instantly. This PR makes brains not food, so that they can't be eaten instantly. # Changelog :cl: - tweak: Brains now can no longer be eaten. --- Resources/Prototypes/Body/Organs/diona.yml | 13 +++++++------ Resources/Prototypes/Body/Organs/human.yml | 16 ++++++++-------- Resources/Prototypes/Body/Organs/slime.yml | 4 ++-- 3 files changed, 17 insertions(+), 16 deletions(-) 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 From 705a5f3d74d9dd3d42bbc2f9fbbf2f61d92b7a17 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Sun, 13 Oct 2024 18:51:51 +0000 Subject: [PATCH 13/19] Automatic Changelog Update (#1044) --- Resources/Changelog/Changelog.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index fe8dd2b2cd..d084a517ec 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7195,3 +7195,10 @@ Entries: 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 From 20c1cccbf9993995b2ea7045931ad68bff5c4030 Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Sun, 13 Oct 2024 15:25:05 -0400 Subject: [PATCH 14/19] Transhumanism Physical Traits (#1035) # Description This PR adds several new "High Cost Physical Traits", that each follows a theme of Transhumanism. Bionic augmentations that provide the user some benefit, typically at a very high cost.

Media

![image](https://github.com/user-attachments/assets/f2d0b007-5632-45a6-87a5-c4a677742fd6) ![image](https://github.com/user-attachments/assets/ebd336f5-df98-4ef4-a4f1-edbdfdfda27d) ![image](https://github.com/user-attachments/assets/e9ff5d13-099e-4004-b2cd-162601bddfc3) ![image](https://github.com/user-attachments/assets/cb3dfc03-72f9-4d9a-9897-22d14a579787)

# Changelog :cl: - add: Added 8 new Physical Traits. These are, Cyber-Eyes Basic System(Plus 4 different modular options), Bionic Arm, Dermal Armor, and Platelet Factories. --------- Signed-off-by: VMSolidus Co-authored-by: DEATHB4DEFEAT <77995199+DEATHB4DEFEAT@users.noreply.github.com> --- .../Assorted/Components/CyberEyesComponent.cs | 11 + .../Assorted/Systems/CyberEyesSystem.cs | 21 ++ Resources/Locale/en-US/traits/misc.ftl | 1 + Resources/Locale/en-US/traits/traits.ftl | 43 +++- Resources/Prototypes/Damage/modifier_sets.yml | 7 + Resources/Prototypes/Traits/physical.yml | 188 ++++++++++++++++++ 6 files changed, 270 insertions(+), 1 deletion(-) create mode 100644 Content.Shared/Traits/Assorted/Components/CyberEyesComponent.cs create mode 100644 Content.Shared/Traits/Assorted/Systems/CyberEyesSystem.cs create mode 100644 Resources/Locale/en-US/traits/misc.ftl 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/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/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/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 From 782f5907afdbdf93faf26d020e09b640a51ee292 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Sun, 13 Oct 2024 19:25:31 +0000 Subject: [PATCH 15/19] Automatic Changelog Update (#1035) --- Resources/Changelog/Changelog.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index d084a517ec..3cadadd7aa 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7202,3 +7202,13 @@ Entries: 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 From 977fccffa9508b7757a94003118f38de403d94b4 Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Sun, 13 Oct 2024 15:26:23 -0400 Subject: [PATCH 16/19] Service Contractor Loadouts (#1041) # Description This PR adds several new "Contractor" uniforms to service roles, as well as several job specific loadout categories. These make use of sprites taken from Aurora, as well as corporations from Aurora. Currently no jumpskirts are available for these. # TODO

Media

![image](https://github.com/user-attachments/assets/f6ce6471-6da5-4881-b730-8fe08c2d630b) ![image](https://github.com/user-attachments/assets/89e30b8c-5a86-489e-8c22-13459aa2b08e)

# Changelog :cl: - add: 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. --- .../Locale/en-US/loadouts/categories.ftl | 3 + .../Locale/en-US/loadouts/itemgroups.ftl | 27 ++ .../CharacterItemGroups/scienceGroups.yml | 19 + .../CharacterItemGroups/serviceGroups.yml | 85 +++++ .../Entities/Clothing/Uniforms/jumpsuits.yml | 129 ++++--- .../Loadouts/Categories/categories.yml | 12 + .../Prototypes/Loadouts/Jobs/science.yml | 268 +++++++++----- .../Prototypes/Loadouts/Jobs/service.yml | 346 +++++++++++++++++- 8 files changed, 723 insertions(+), 166 deletions(-) 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/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/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/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 From 120f73d3a489e0fb07405b66559a9b59eac110d0 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Sun, 13 Oct 2024 19:26:45 +0000 Subject: [PATCH 17/19] Automatic Changelog Update (#1041) --- Resources/Changelog/Changelog.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 3cadadd7aa..8b9597f320 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7212,3 +7212,15 @@ Entries: 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 From 6a815ba315e87b5337afcf1966d66748c0236b0f Mon Sep 17 00:00:00 2001 From: Mnemotechnican <69920617+Mnemotechnician@users.noreply.github.com> Date: Sun, 13 Oct 2024 22:38:19 +0300 Subject: [PATCH 18/19] Feat: Port All Delta-V Mail Improvements (#1011) # Description Showing some love to our hard-working couriers. This cherry-picks the following PRs from delta-v: - https://github.com/DeltaV-Station/Delta-v/pull/1472 - Adds a MailMetrics PDA cartridge for couriers and the LO - https://github.com/DeltaV-Station/Delta-v/pull/1652 - Ports mail tweaks from frontier to delta-v, adding large packages, RPDs (mail cannons), and more mail - https://github.com/DeltaV-Station/Delta-v/pull/1788 - Adding a couple more packages - https://github.com/DeltaV-Station/Delta-v/pull/1925 - Fixing the non-functional "last known location" part of mail descriptions Some mail items, such as the opporozidone syringe and rainbow joints/blunts, had to be disabled because we don't have them (yet?)

Media

![image](https://github.com/user-attachments/assets/2299ffc3-17ce-4743-886c-7705e8789e96) ![image](https://github.com/user-attachments/assets/a2ee1a07-d765-41e7-8f70-f94a50412398) ![image](https://github.com/user-attachments/assets/03b16b8e-0eff-4332-99ad-d4a201ee8f2b) ![image](https://github.com/user-attachments/assets/44d2e5c2-db79-4d8d-8877-693648edd35d) ![image](https://github.com/user-attachments/assets/f3d7bdbb-fc14-4f2f-bf30-aa54fdf49099) ![image](https://github.com/user-attachments/assets/b1e4c53f-c9d8-496b-a72e-f4f4b702e368)

--- # Changelog :cl: - add: 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. - add: The list of possible mail packages has been greately expanded, and now includes large parcels. - add: The CourierDrobe now offers a rapid mail delivery device, along with capsules for it. --------- Signed-off-by: Adeinitas <147965189+adeinitas@users.noreply.github.com> Co-authored-by: portfiend <109661617+portfiend@users.noreply.github.com> Co-authored-by: byte <50130120+huckleton@users.noreply.github.com> Co-authored-by: deltanedas <39013340+deltanedas@users.noreply.github.com> Co-authored-by: Adeinitas <147965189+adeinitas@users.noreply.github.com> Co-authored-by: ErhardSteinhauer <65374927+ErhardSteinhauer@users.noreply.github.com> Co-authored-by: Dvir Co-authored-by: Dvir <39403717+dvir001@users.noreply.github.com> Co-authored-by: Whatstone <166147148+whatston3@users.noreply.github.com> Co-authored-by: Whatstone Co-authored-by: Danger Revolution! <142105406+DangerRevolution@users.noreply.github.com> Co-authored-by: Milon --- .../Cartridges/MailMetricUi.cs | 28 + .../Cartridges/MailMetricUiFragment.xaml | 183 ++ .../Cartridges/MailMetricUiFragment.xaml.cs | 104 ++ .../StationLogisticStatsDatabaseComponent.cs | 14 + .../Cargo/Systems/LogisticStatsSystem.cs | 63 + .../MailMetricsCartridgeComponent.cs | 11 + .../Cartridges/MailMetricsCartridgeSystem.cs | 82 + .../Mail/Components/DelayedItemComponent.cs | 15 + .../Mail/Components/MailComponent.cs | 111 ++ .../Mail/Components/MailReceiverComponent.cs | 4 + .../Components/MailTeleporterComponent.cs | 118 ++ .../Components/StationMailRouterComponent.cs | 4 +- .../{Nyanotrasen => }/Mail/MailCommands.cs | 39 +- Content.Server/Mail/MailConstants.cs | 37 + .../Mail/Systems/DelayedItemSystem.cs | 43 + Content.Server/Mail/Systems/MailSystem.cs | 756 ++++++++ .../Mail/Components/MailComponent.cs | 108 -- .../Mail/Components/MailReceiverComponent.cs | 6 - .../Components/MailTeleporterComponent.cs | 108 -- Content.Server/Nyanotrasen/Mail/MailSystem.cs | 731 -------- .../Cartridges/MailMetricUiState.cs | 50 + .../deltav/cartridge-loader/cartridges.ftl | 13 + .../{Mail/mail.ftl => mail/commands.ftl} | 26 +- Resources/Locale/en-US/mail/mail.ftl | 23 + Resources/Migrations/frontierMigrations.yml | 2 + .../Inventories/courierdrobe.yml | 12 +- .../Entities/Objects/Devices/cartridges.yml | 21 + .../Entities/Objects/Specific/Mail/mail.yml | 1641 +++++++++++++++++ .../Objects/Specific/Mail/mail_civilian.yml | 88 +- .../Objects/Specific/Mail/mail_command.yml | 35 + .../Specific/Mail/mail_engineering.yml | 182 ++ .../Specific/Mail/mail_epistemology.yml | 10 - .../Objects/Specific/Mail/mail_medical.yml | 17 + .../Objects/Specific/Mail/mail_security.yml | 41 +- .../Prototypes/DeltaV/Mail/mailDeliveries.yml | 147 ++ .../Entities/Objects/Devices/pda.yml | 6 + .../Entities/Objects/Misc/mail_capsule.yml | 139 ++ .../Objects/Specific/Mail/Items/boxes.yml | 212 +++ .../Objects/Specific/Mail/Items/misc.yml | 117 ++ .../Objects/Specific/Mail/Items/paper.yml | 483 +++++ .../Objects/Specific/Mail/base_mail_large.yml | 79 + .../Weapons/Guns/Launchers/launchers.yml | 26 + .../Fills/Vending/Inventories/maildrobe.yml | 2 + .../Entities/Objects/Devices/pda.yml | 10 +- .../Objects/Specific/Mail/base_mail.yml | 20 +- .../Entities/Objects/Specific/Mail/mail.yml | 835 --------- .../Objects/Specific/Mail/mail_command.yml | 9 - .../Specific/Mail/mail_engineering.yml | 45 - .../Specific/Mail/mail_specific_items.yml | 169 -- .../Nyanotrasen/Entities/Stations/mail.yml | 1 + Resources/Prototypes/Recipes/Lathes/misc.yml | 16 + Resources/Prototypes/tags.yml | 21 + .../Devices/cartridge.rsi/cart-mail.png | Bin 0 -> 242 bytes .../Objects/Devices/cartridge.rsi/meta.json | 7 +- .../Misc/mail_capsule.rsi/icon-cash.png | Bin 0 -> 6954 bytes .../Misc/mail_capsule.rsi/icon-empty.png | Bin 0 -> 6506 bytes .../Misc/mail_capsule.rsi/icon-food.png | Bin 0 -> 7202 bytes .../Misc/mail_capsule.rsi/icon-mail.png | Bin 0 -> 6821 bytes .../Objects/Misc/mail_capsule.rsi/meta.json | 26 + .../Objects/Misc/mail_capsule.rsi/spent.png | Bin 0 -> 5618 bytes .../Objects/Specific/Mail/mail.rsi/broken.png | Bin .../Specific/Mail/mail.rsi/fragile.png | Bin .../Objects/Specific/Mail/mail.rsi/icon.png | Bin .../Objects/Specific/Mail/mail.rsi/locked.png | Bin .../Objects/Specific/Mail/mail.rsi/meta.json | 0 .../Specific/Mail/mail.rsi/postmark.png | Bin .../Specific/Mail/mail.rsi/priority.png | Bin .../Mail/mail.rsi/priority_inactive.png | Bin .../Objects/Specific/Mail/mail.rsi/trash.png | Bin .../Specific/Mail/mail_large.rsi/broken.png | Bin 0 -> 246 bytes .../Specific/Mail/mail_large.rsi/fragile.png | Bin 0 -> 135 bytes .../Specific/Mail/mail_large.rsi/icon.png | Bin 0 -> 304 bytes .../Mail/mail_large.rsi/inhand-left.png | Bin 0 -> 324 bytes .../Mail/mail_large.rsi/inhand-right.png | Bin 0 -> 324 bytes .../Specific/Mail/mail_large.rsi/locked.png | Bin 0 -> 229 bytes .../Specific/Mail/mail_large.rsi/meta.json | 40 + .../Specific/Mail/mail_large.rsi/priority.png | Bin 0 -> 117 bytes .../Mail/mail_large.rsi/priority_inactive.png | Bin 0 -> 118 bytes .../Specific/Mail/mail_large.rsi/trash.png | Bin 0 -> 343 bytes .../Guns/Launchers/mail.rsi/bolt-open.png | Bin 0 -> 6861 bytes .../Launchers/mail.rsi/equipped-BACKPACK.png | Bin 0 -> 1472 bytes .../Guns/Launchers/mail.rsi/equipped-BELT.png | Bin 0 -> 1488 bytes .../Weapons/Guns/Launchers/mail.rsi/icon.png | Bin 0 -> 6855 bytes .../Guns/Launchers/mail.rsi/inhand-left.png | Bin 0 -> 1048 bytes .../Guns/Launchers/mail.rsi/inhand-right.png | Bin 0 -> 1060 bytes .../Weapons/Guns/Launchers/mail.rsi/meta.json | 33 + 86 files changed, 5070 insertions(+), 2099 deletions(-) create mode 100644 Content.Client/CartridgeLoader/Cartridges/MailMetricUi.cs create mode 100644 Content.Client/CartridgeLoader/Cartridges/MailMetricUiFragment.xaml create mode 100644 Content.Client/CartridgeLoader/Cartridges/MailMetricUiFragment.xaml.cs create mode 100644 Content.Server/Cargo/Components/StationLogisticStatsDatabaseComponent.cs create mode 100644 Content.Server/Cargo/Systems/LogisticStatsSystem.cs create mode 100644 Content.Server/CartridgeLoader/Cartridges/MailMetricsCartridgeComponent.cs create mode 100644 Content.Server/CartridgeLoader/Cartridges/MailMetricsCartridgeSystem.cs create mode 100644 Content.Server/Mail/Components/DelayedItemComponent.cs create mode 100644 Content.Server/Mail/Components/MailComponent.cs create mode 100644 Content.Server/Mail/Components/MailReceiverComponent.cs create mode 100644 Content.Server/Mail/Components/MailTeleporterComponent.cs rename Content.Server/{Nyanotrasen => }/Mail/Components/StationMailRouterComponent.cs (51%) rename Content.Server/{Nyanotrasen => }/Mail/MailCommands.cs (69%) create mode 100644 Content.Server/Mail/MailConstants.cs create mode 100644 Content.Server/Mail/Systems/DelayedItemSystem.cs create mode 100644 Content.Server/Mail/Systems/MailSystem.cs delete mode 100644 Content.Server/Nyanotrasen/Mail/Components/MailComponent.cs delete mode 100644 Content.Server/Nyanotrasen/Mail/Components/MailReceiverComponent.cs delete mode 100644 Content.Server/Nyanotrasen/Mail/Components/MailTeleporterComponent.cs delete mode 100644 Content.Server/Nyanotrasen/Mail/MailSystem.cs create mode 100644 Content.Shared/DeltaV/CartridgeLoader/Cartridges/MailMetricUiState.cs rename Resources/Locale/en-US/{Mail/mail.ftl => mail/commands.ftl} (52%) create mode 100644 Resources/Locale/en-US/mail/mail.ftl create mode 100644 Resources/Migrations/frontierMigrations.yml rename Resources/Prototypes/{Nyanotrasen => DeltaV}/Entities/Objects/Specific/Mail/mail_civilian.yml (71%) create mode 100644 Resources/Prototypes/DeltaV/Entities/Objects/Specific/Mail/mail_command.yml create mode 100644 Resources/Prototypes/DeltaV/Entities/Objects/Specific/Mail/mail_engineering.yml rename Resources/Prototypes/{Nyanotrasen => DeltaV}/Entities/Objects/Specific/Mail/mail_epistemology.yml (74%) rename Resources/Prototypes/{Nyanotrasen => DeltaV}/Entities/Objects/Specific/Mail/mail_medical.yml (84%) rename Resources/Prototypes/{Nyanotrasen => DeltaV}/Entities/Objects/Specific/Mail/mail_security.yml (50%) create mode 100644 Resources/Prototypes/DeltaV/Mail/mailDeliveries.yml create mode 100644 Resources/Prototypes/Entities/Objects/Misc/mail_capsule.yml create mode 100644 Resources/Prototypes/Entities/Objects/Specific/Mail/Items/boxes.yml create mode 100644 Resources/Prototypes/Entities/Objects/Specific/Mail/Items/misc.yml create mode 100644 Resources/Prototypes/Entities/Objects/Specific/Mail/Items/paper.yml create mode 100644 Resources/Prototypes/Entities/Objects/Specific/Mail/base_mail_large.yml delete mode 100644 Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Mail/mail.yml delete mode 100644 Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Mail/mail_command.yml delete mode 100644 Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Mail/mail_engineering.yml delete mode 100644 Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Mail/mail_specific_items.yml create mode 100644 Resources/Textures/DeltaV/Objects/Devices/cartridge.rsi/cart-mail.png create mode 100644 Resources/Textures/Objects/Misc/mail_capsule.rsi/icon-cash.png create mode 100644 Resources/Textures/Objects/Misc/mail_capsule.rsi/icon-empty.png create mode 100644 Resources/Textures/Objects/Misc/mail_capsule.rsi/icon-food.png create mode 100644 Resources/Textures/Objects/Misc/mail_capsule.rsi/icon-mail.png create mode 100644 Resources/Textures/Objects/Misc/mail_capsule.rsi/meta.json create mode 100644 Resources/Textures/Objects/Misc/mail_capsule.rsi/spent.png rename Resources/Textures/{Nyanotrasen => }/Objects/Specific/Mail/mail.rsi/broken.png (100%) rename Resources/Textures/{Nyanotrasen => }/Objects/Specific/Mail/mail.rsi/fragile.png (100%) rename Resources/Textures/{Nyanotrasen => }/Objects/Specific/Mail/mail.rsi/icon.png (100%) rename Resources/Textures/{Nyanotrasen => }/Objects/Specific/Mail/mail.rsi/locked.png (100%) rename Resources/Textures/{Nyanotrasen => }/Objects/Specific/Mail/mail.rsi/meta.json (100%) rename Resources/Textures/{Nyanotrasen => }/Objects/Specific/Mail/mail.rsi/postmark.png (100%) rename Resources/Textures/{Nyanotrasen => }/Objects/Specific/Mail/mail.rsi/priority.png (100%) rename Resources/Textures/{Nyanotrasen => }/Objects/Specific/Mail/mail.rsi/priority_inactive.png (100%) rename Resources/Textures/{Nyanotrasen => }/Objects/Specific/Mail/mail.rsi/trash.png (100%) create mode 100644 Resources/Textures/Objects/Specific/Mail/mail_large.rsi/broken.png create mode 100644 Resources/Textures/Objects/Specific/Mail/mail_large.rsi/fragile.png create mode 100644 Resources/Textures/Objects/Specific/Mail/mail_large.rsi/icon.png create mode 100644 Resources/Textures/Objects/Specific/Mail/mail_large.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Specific/Mail/mail_large.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Specific/Mail/mail_large.rsi/locked.png create mode 100644 Resources/Textures/Objects/Specific/Mail/mail_large.rsi/meta.json create mode 100644 Resources/Textures/Objects/Specific/Mail/mail_large.rsi/priority.png create mode 100644 Resources/Textures/Objects/Specific/Mail/mail_large.rsi/priority_inactive.png create mode 100644 Resources/Textures/Objects/Specific/Mail/mail_large.rsi/trash.png create mode 100644 Resources/Textures/Objects/Weapons/Guns/Launchers/mail.rsi/bolt-open.png create mode 100644 Resources/Textures/Objects/Weapons/Guns/Launchers/mail.rsi/equipped-BACKPACK.png create mode 100644 Resources/Textures/Objects/Weapons/Guns/Launchers/mail.rsi/equipped-BELT.png create mode 100644 Resources/Textures/Objects/Weapons/Guns/Launchers/mail.rsi/icon.png create mode 100644 Resources/Textures/Objects/Weapons/Guns/Launchers/mail.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Weapons/Guns/Launchers/mail.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Weapons/Guns/Launchers/mail.rsi/meta.json 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.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.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/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/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/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/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/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/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/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/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 0000000000000000000000000000000000000000..5734abb6fd1b5b7e9caefc5898adfe5d454c4759 GIT binary patch literal 242 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCiji0(?STO$?F~(`I`6H?_1h^!In~*|X!ujVnM&R>^4*K#HRz$S?Rm1Tfrd z-Wv%N<1FxqEM{QfI|9OtQ?>b|fr9>?E{-7<{%_A)@*Pm%aS0U8|GwXlaSexe#+{{) z%A4a7Pju@={N>T;+IdTCt41fwJV}N#N@o~4X0236Sk-n=n&~O;Q+=%uqWqgp{9+A? Z-mhttJo_hemJQHo22WQ%mvv4FO#p~3QWO9H literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..8ba33c95b571476a108f05f30de91aa581c78353 GIT binary patch literal 6954 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..085b787410b5a3501a456472d2bf9f23a1d99990 GIT binary patch literal 6506 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 DcPr_! literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..d08489cec899042a9f0875d207ef925a6a15f1c9 GIT binary patch literal 7202 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..b35a2acc0f35c17db2ed3b9dc71defa390b8a36b GIT binary patch literal 6821 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 R)<7u$qg&Zq9$C2|_Fs*mhuZ)E literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..acd0d0577fcf2c69dafee98bdeea2787236ac4f8 GIT binary patch literal 5618 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..1c798c4075b0ce9759cf88d64e2def45dea7342c GIT binary patch literal 246 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJy`CJRkaFj2;(rr(q0kZ~c z^y)qT4xd>(ZRX+xk)EbNhx&vCI$c3cp(+&@*SlnQZ2Pr;kJ^|0jhkN7GpAnsU$Rx^ zW>U&&ZJ|9YUJ0x*71_lq%e`%4sCHjdpaB;f?}6|shu}HQp5$F*HPgg&ebxsLQ07`vh+W-In literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..0917000cbef3ef7f6341f1f764d978392db98bd9 GIT binary patch literal 135 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzKTj9Okcigg1PRu~2_ijBfd*V| z2Q?s^k^>#S8>Tw`v$ylT%EQ0$l`^}*!$}@Dw=^6Kh@A|?!sq;eoTzVy4mPwO0P11p ZV|Z!Jv#PmvZWYjE22WQ%mvv4FO#mgMEY$!2 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..f3974ab116c90710a0a437b09a83978350e0439a GIT binary patch literal 304 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyegQrqu0T4_R5H>|Eyi9s*+nD8 zRXx>JGtFH)!(A)eOFPF~FV9;i-$%E|-=HDdsx`sBGs(Uy#i>8td19vf%(l|mMZOEm z1DDr?ZkW|R-N-0eiPZ#XqEtzcUogXeT!203uL4k2xu=U`h(+(wix;^X6nI(_Z7;e8 zTyzuP`+xE_k;_S^TNcP4iLKFCQ9Q}KNYj3mXhJ|Xzc-TLq|*Wk||bFk8O;!1DBuXZ|q)&HC|)L3=`Y TQH{n&caX`Ru6{1-oD!Mzc-TLq|*Wk||bFk8O;!1DBuXZ|q)&HC|)L3=`Y TQH{n&caX`Ru6{1-oD!Mn3Iu0Yz-(=yUdEyi9s*+nD8 zRXx>JGtFH)!(A)eOFPF~FV9;i-$%E|-=HDdsx`sBGs(Uy#i>8td19vf%(l|mMZOEm z1DDr?ZkW|R-N?vRN2)2p#6VfpSXB&Y@;s4MWk5>4B*-tA;XhvB?m1T!s8qt!#WBR9 rH#tE11>klOa_KuA`G4kMR(2s)i8Lv`njxgN@xNA<4sdi literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..9c5a74ad10326820f316db28068a15bd0c649474 GIT binary patch literal 117 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz8&4O$Dny@!}XRX7Ap9zKQcYZt5kn&CHlpX((tVwB4%^oFtgN$UjZ)xx}eD(d)f@&b1 Mr>mdKI;Vst0DGk+(EtDd literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..fc03165b576885291ea0dfaf8b8f2385af9cd7a2 GIT binary patch literal 118 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzTTd6qkcigg1PRu~47^8vX#8ES z+RHbi!1Bn2j`?aP7x^se-zodP2sGewJE#FvlOWR51m%=4Fzj+QE7arl)BzgA;OXk; Jvd$@?2>=f&Ay)tZ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..2ef4ee7233883af6f8d42a12d2ca927b3ec865b6 GIT binary patch literal 343 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 ej6g(0_3;yTz>Vz2R4ZfY literal 0 HcmV?d00001 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 From 1bb7f1b7dec4d1a71097e7ca655e9c243913198e Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Sun, 13 Oct 2024 19:38:44 +0000 Subject: [PATCH 19/19] Automatic Changelog Update (#1011) --- Resources/Changelog/Changelog.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 8b9597f320..96656ab107 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7224,3 +7224,21 @@ Entries: 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