diff --git a/Content.Server/Lathe/Components/LatheAnnouncingComponent.cs b/Content.Server/Lathe/Components/LatheAnnouncingComponent.cs new file mode 100644 index 0000000000..16c30d98eb --- /dev/null +++ b/Content.Server/Lathe/Components/LatheAnnouncingComponent.cs @@ -0,0 +1,17 @@ +using Content.Shared.Radio; +using Robust.Shared.Prototypes; + +namespace Content.Server.Lathe.Components; + +/// +/// Causes this entity to announce onto the provided channels when it receives new recipes from its server +/// +[RegisterComponent] +public sealed partial class LatheAnnouncingComponent : Component +{ + /// + /// Radio channels to broadcast to when a new set of recipes is received + /// + [DataField(required: true)] + public List> Channels = new(); +} diff --git a/Content.Server/Lathe/LatheSystem.cs b/Content.Server/Lathe/LatheSystem.cs index 68a5228bdf..4851f6b63d 100644 --- a/Content.Server/Lathe/LatheSystem.cs +++ b/Content.Server/Lathe/LatheSystem.cs @@ -8,6 +8,7 @@ using Content.Server.Materials; using Content.Server.Popups; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; +using Content.Server.Radio.EntitySystems; using Content.Server.Stack; using Content.Shared.Atmos; using Content.Shared.Chemistry.Components; @@ -20,6 +21,7 @@ using Content.Shared.Emag.Systems; using Content.Shared.Examine; using Content.Shared.Lathe; using Content.Shared.Lathe.Prototypes; +using Content.Shared.Localizations; using Content.Shared.Materials; using Content.Shared.Power; using Content.Shared.ReagentSpeed; @@ -53,6 +55,7 @@ namespace Content.Server.Lathe [Dependency] private readonly SharedSolutionContainerSystem _solution = default!; [Dependency] private readonly StackSystem _stack = default!; [Dependency] private readonly TransformSystem _transform = default!; + [Dependency] private readonly RadioSystem _radio = default!; /// /// Per-tick cache @@ -67,6 +70,7 @@ namespace Content.Server.Lathe SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnPowerChanged); SubscribeLocalEvent(OnDatabaseModified); + SubscribeLocalEvent(OnTechnologyDatabaseModified); SubscribeLocalEvent(OnResearchRegistrationChanged); SubscribeLocalEvent(OnLatheQueueRecipeMessage); @@ -364,6 +368,41 @@ namespace Content.Server.Lathe UpdateUserInterfaceState(uid, component); } + private void OnTechnologyDatabaseModified(Entity ent, ref TechnologyDatabaseModifiedEvent args) + { + if (args.NewlyUnlockedRecipes is null) + return; + + if (!TryGetAvailableRecipes(ent.Owner, out var potentialRecipes)) + return; + + var recipeNames = new List(); + foreach (var recipeId in args.NewlyUnlockedRecipes) + { + if (!potentialRecipes.Contains(new(recipeId))) + continue; + + if (!_proto.TryIndex(recipeId, out LatheRecipePrototype? recipe)) + continue; + + var itemName = GetRecipeName(recipe!); + recipeNames.Add(Loc.GetString("lathe-unlock-recipe-radio-broadcast-item", ("item", itemName))); + } + + if (recipeNames.Count == 0) + return; + + var message = Loc.GetString( + "lathe-unlock-recipe-radio-broadcast", + ("items", ContentLocalizationManager.FormatList(recipeNames)) + ); + + foreach (var channel in ent.Comp.Channels) + { + _radio.SendRadioMessage(ent.Owner, message, channel, ent.Owner, escapeMarkup: false); + } + } + private void OnResearchRegistrationChanged(EntityUid uid, LatheComponent component, ref ResearchRegistrationChangedEvent args) { UpdateUserInterfaceState(uid, component); diff --git a/Content.Server/Research/Systems/ResearchSystem.Technology.cs b/Content.Server/Research/Systems/ResearchSystem.Technology.cs index 360985b4b6..0237c8712a 100644 --- a/Content.Server/Research/Systems/ResearchSystem.Technology.cs +++ b/Content.Server/Research/Systems/ResearchSystem.Technology.cs @@ -119,15 +119,17 @@ public sealed partial class ResearchSystem } component.UnlockedTechnologies.Add(technology.ID); + var addedRecipes = new List(); foreach (var unlock in technology.RecipeUnlocks) { if (component.UnlockedRecipes.Contains(unlock)) continue; component.UnlockedRecipes.Add(unlock); + addedRecipes.Add(unlock); } Dirty(uid, component); - var ev = new TechnologyDatabaseModifiedEvent(); + var ev = new TechnologyDatabaseModifiedEvent(addedRecipes); RaiseLocalEvent(uid, ref ev); } diff --git a/Content.Shared/Research/Components/TechnologyDatabaseComponent.cs b/Content.Shared/Research/Components/TechnologyDatabaseComponent.cs index 4f976869f7..a69e4ee9dd 100644 --- a/Content.Shared/Research/Components/TechnologyDatabaseComponent.cs +++ b/Content.Shared/Research/Components/TechnologyDatabaseComponent.cs @@ -54,7 +54,7 @@ public sealed partial class TechnologyDatabaseComponent : Component /// server to all of it's clients. /// [ByRefEvent] -public readonly record struct TechnologyDatabaseModifiedEvent; +public readonly record struct TechnologyDatabaseModifiedEvent(List? NewlyUnlockedRecipes); /// /// Event raised on a database after being synchronized diff --git a/Content.Shared/Research/Systems/BlueprintSystem.cs b/Content.Shared/Research/Systems/BlueprintSystem.cs index 237ff70300..903e529089 100644 --- a/Content.Shared/Research/Systems/BlueprintSystem.cs +++ b/Content.Shared/Research/Systems/BlueprintSystem.cs @@ -7,6 +7,7 @@ using Content.Shared.Research.Prototypes; using Content.Shared.Whitelist; using Robust.Shared.Containers; using Robust.Shared.Prototypes; +using System.Linq; namespace Content.Shared.Research.Systems; @@ -64,7 +65,7 @@ public sealed class BlueprintSystem : EntitySystem _container.Insert(blueprint.Owner, _container.GetContainer(ent, ent.Comp.ContainerId)); - var ev = new TechnologyDatabaseModifiedEvent(); + var ev = new TechnologyDatabaseModifiedEvent(blueprint.Comp.ProvidedRecipes.Select(it => it.Id).ToList()); RaiseLocalEvent(ent, ref ev); return true; } diff --git a/Content.Shared/Research/Systems/SharedResearchSystem.cs b/Content.Shared/Research/Systems/SharedResearchSystem.cs index bca1ae4888..7ed33f7204 100644 --- a/Content.Shared/Research/Systems/SharedResearchSystem.cs +++ b/Content.Shared/Research/Systems/SharedResearchSystem.cs @@ -301,7 +301,7 @@ public abstract class SharedResearchSystem : EntitySystem component.UnlockedRecipes.Add(recipe); Dirty(uid, component); - var ev = new TechnologyDatabaseModifiedEvent(); + var ev = new TechnologyDatabaseModifiedEvent(new List { recipe }); RaiseLocalEvent(uid, ref ev); } } diff --git a/Resources/Locale/en-US/lathe/lathesystem.ftl b/Resources/Locale/en-US/lathe/lathesystem.ftl index 9fa62e0c1e..93e0107f68 100644 --- a/Resources/Locale/en-US/lathe/lathesystem.ftl +++ b/Resources/Locale/en-US/lathe/lathesystem.ftl @@ -1 +1,3 @@ lathe-popup-material-not-used = This material is not used in this machine. +lathe-unlock-recipe-radio-broadcast = This lathe is now capable of producing the following recipes: {$items} +lathe-unlock-recipe-radio-broadcast-item = [bold]{$item}[/bold] diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index cf3bcac302..0d008ed1e1 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -408,6 +408,8 @@ - Sheet - RawMaterial - Ingot + - type: LatheAnnouncing + channels: [Security] - type: entity id: AmmoTechFab @@ -478,6 +480,8 @@ board: MedicalTechFabCircuitboard - type: StealTarget stealGroup: MedicalTechFabCircuitboard + - type: LatheAnnouncing + channels: [Medical] - type: entity parent: BaseLathe