From 85b7d183bd9010feab003af1cef0980790d6cd7a Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Sun, 25 Aug 2024 08:06:50 -0400 Subject: [PATCH] Blueprints (#31138) * Blueprints * Update tables_loot.yml * doink * mark as required --- Content.Server/Lathe/LatheSystem.cs | 4 +- Content.Shared/Lathe/LatheComponent.cs | 2 +- .../Research/Components/BlueprintComponent.cs | 19 +++ .../Components/BlueprintReceiverComponent.cs | 18 +++ .../Research/Systems/BlueprintSystem.cs | 114 ++++++++++++++++++ .../en-US/research/components/blueprint.ftl | 2 + .../Spawners/Random/Salvage/tables_loot.yml | 3 + .../Entities/Objects/Tools/blueprint.yml | 49 ++++++++ .../Entities/Structures/Machines/lathe.yml | 14 ++- .../Prototypes/Recipes/Lathes/salvage.yml | 14 ++- Resources/Prototypes/tags.yml | 3 + .../Objects/Tools/blueprint.rsi/icon.png | Bin 0 -> 449 bytes .../Tools/blueprint.rsi/inhand-left.png | Bin 0 -> 341 bytes .../Tools/blueprint.rsi/inhand-right.png | Bin 0 -> 358 bytes .../Objects/Tools/blueprint.rsi/meta.json | 22 ++++ 15 files changed, 259 insertions(+), 5 deletions(-) create mode 100644 Content.Shared/Research/Components/BlueprintComponent.cs create mode 100644 Content.Shared/Research/Components/BlueprintReceiverComponent.cs create mode 100644 Content.Shared/Research/Systems/BlueprintSystem.cs create mode 100644 Resources/Locale/en-US/research/components/blueprint.ftl create mode 100644 Resources/Prototypes/Entities/Objects/Tools/blueprint.yml create mode 100644 Resources/Textures/Objects/Tools/blueprint.rsi/icon.png create mode 100644 Resources/Textures/Objects/Tools/blueprint.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Tools/blueprint.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Tools/blueprint.rsi/meta.json diff --git a/Content.Server/Lathe/LatheSystem.cs b/Content.Server/Lathe/LatheSystem.cs index 5ece533a62..6d7d4e4533 100644 --- a/Content.Server/Lathe/LatheSystem.cs +++ b/Content.Server/Lathe/LatheSystem.cs @@ -155,10 +155,10 @@ namespace Content.Server.Lathe { var ev = new LatheGetRecipesEvent(uid, getUnavailable) { - Recipes = new List>(component.StaticRecipes) + Recipes = new HashSet>(component.StaticRecipes) }; RaiseLocalEvent(uid, ev); - return ev.Recipes; + return ev.Recipes.ToList(); } public static List> GetAllBaseRecipes(LatheComponent component) diff --git a/Content.Shared/Lathe/LatheComponent.cs b/Content.Shared/Lathe/LatheComponent.cs index 7924a0ec94..de4311e559 100644 --- a/Content.Shared/Lathe/LatheComponent.cs +++ b/Content.Shared/Lathe/LatheComponent.cs @@ -83,7 +83,7 @@ namespace Content.Shared.Lathe public bool getUnavailable; - public List> Recipes = new(); + public HashSet> Recipes = new(); public LatheGetRecipesEvent(EntityUid lathe, bool forced) { diff --git a/Content.Shared/Research/Components/BlueprintComponent.cs b/Content.Shared/Research/Components/BlueprintComponent.cs new file mode 100644 index 0000000000..71ed3da034 --- /dev/null +++ b/Content.Shared/Research/Components/BlueprintComponent.cs @@ -0,0 +1,19 @@ +using Content.Shared.Research.Prototypes; +using Content.Shared.Research.Systems; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Research.Components; + +/// +/// This is used for an item that is inserted directly into a given lathe to provide it with a recipe. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(BlueprintSystem))] +public sealed partial class BlueprintComponent : Component +{ + /// + /// The recipes that this blueprint provides. + /// + [DataField(required: true)] + public HashSet> ProvidedRecipes = new(); +} diff --git a/Content.Shared/Research/Components/BlueprintReceiverComponent.cs b/Content.Shared/Research/Components/BlueprintReceiverComponent.cs new file mode 100644 index 0000000000..94c323eb86 --- /dev/null +++ b/Content.Shared/Research/Components/BlueprintReceiverComponent.cs @@ -0,0 +1,18 @@ +using Content.Shared.Research.Systems; +using Content.Shared.Whitelist; +using Robust.Shared.GameStates; + +namespace Content.Shared.Research.Components; + +/// +/// This is used for a lathe that can utilize s to gain more recipes. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(BlueprintSystem))] +public sealed partial class BlueprintReceiverComponent : Component +{ + [DataField] + public string ContainerId = "blueprint"; + + [DataField(required: true)] + public EntityWhitelist Whitelist = new(); +} diff --git a/Content.Shared/Research/Systems/BlueprintSystem.cs b/Content.Shared/Research/Systems/BlueprintSystem.cs new file mode 100644 index 0000000000..237ff70300 --- /dev/null +++ b/Content.Shared/Research/Systems/BlueprintSystem.cs @@ -0,0 +1,114 @@ +using Content.Shared.IdentityManagement; +using Content.Shared.Interaction; +using Content.Shared.Lathe; +using Content.Shared.Popups; +using Content.Shared.Research.Components; +using Content.Shared.Research.Prototypes; +using Content.Shared.Whitelist; +using Robust.Shared.Containers; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Research.Systems; + +public sealed class BlueprintSystem : EntitySystem +{ + [Dependency] private readonly SharedContainerSystem _container = default!; + [Dependency] private readonly EntityWhitelistSystem _entityWhitelist = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + + /// + public override void Initialize() + { + SubscribeLocalEvent(OnStartup); + SubscribeLocalEvent(OnAfterInteract); + SubscribeLocalEvent(OnGetRecipes); + } + + private void OnStartup(Entity ent, ref ComponentStartup args) + { + _container.EnsureContainer(ent, ent.Comp.ContainerId); + } + + private void OnAfterInteract(Entity ent, ref AfterInteractUsingEvent args) + { + if (args.Handled || !args.CanReach || !TryComp(args.Used, out var blueprintComponent)) + return; + args.Handled = TryInsertBlueprint(ent, (args.Used, blueprintComponent), args.User); + } + + private void OnGetRecipes(Entity ent, ref LatheGetRecipesEvent args) + { + var recipes = GetBlueprintRecipes(ent); + foreach (var recipe in recipes) + { + args.Recipes.Add(recipe); + } + } + + public bool TryInsertBlueprint(Entity ent, Entity blueprint, EntityUid? user) + { + if (!CanInsertBlueprint(ent, blueprint, user)) + return false; + + if (user is not null) + { + var userId = Identity.Entity(user.Value, EntityManager); + var bpId = Identity.Entity(blueprint, EntityManager); + var machineId = Identity.Entity(ent, EntityManager); + var msg = Loc.GetString("blueprint-receiver-popup-insert", + ("user", userId), + ("blueprint", bpId), + ("receiver", machineId)); + _popup.PopupPredicted(msg, ent, user); + } + + _container.Insert(blueprint.Owner, _container.GetContainer(ent, ent.Comp.ContainerId)); + + var ev = new TechnologyDatabaseModifiedEvent(); + RaiseLocalEvent(ent, ref ev); + return true; + } + + public bool CanInsertBlueprint(Entity ent, Entity blueprint, EntityUid? user) + { + if (_entityWhitelist.IsWhitelistFail(ent.Comp.Whitelist, blueprint)) + { + return false; + } + + if (blueprint.Comp.ProvidedRecipes.Count == 0) + { + Log.Error($"Attempted to insert blueprint {ToPrettyString(blueprint)} with no recipes."); + return false; + } + + // Don't add new blueprints if there are no new recipes. + var currentRecipes = GetBlueprintRecipes(ent); + if (currentRecipes.Count != 0 && currentRecipes.IsSupersetOf(blueprint.Comp.ProvidedRecipes)) + { + _popup.PopupPredicted(Loc.GetString("blueprint-receiver-popup-recipe-exists"), ent, user); + return false; + } + + return _container.CanInsert(blueprint, _container.GetContainer(ent, ent.Comp.ContainerId)); + } + + public HashSet> GetBlueprintRecipes(Entity ent) + { + var contained = _container.GetContainer(ent, ent.Comp.ContainerId); + + var recipes = new HashSet>(); + foreach (var blueprint in contained.ContainedEntities) + { + if (!TryComp(blueprint, out var blueprintComponent)) + continue; + + foreach (var provided in blueprintComponent.ProvidedRecipes) + { + recipes.Add(provided); + } + } + + return recipes; + } +} diff --git a/Resources/Locale/en-US/research/components/blueprint.ftl b/Resources/Locale/en-US/research/components/blueprint.ftl new file mode 100644 index 0000000000..34c3a3c80e --- /dev/null +++ b/Resources/Locale/en-US/research/components/blueprint.ftl @@ -0,0 +1,2 @@ +blueprint-receiver-popup-insert = { CAPITALIZE(THE($user)) } inserted { THE($blueprint) } into { THE($receiver) }. +blueprint-receiver-popup-recipe-exists = The same blueprint was already inserted! diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/Salvage/tables_loot.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/Salvage/tables_loot.yml index 5e128e8f96..61b3d10817 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/Salvage/tables_loot.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/Salvage/tables_loot.yml @@ -207,6 +207,7 @@ id: SalvageEquipmentRare table: !type:GroupSelector children: + - id: BlueprintFlare - id: FultonBeacon - id: Fulton amount: !type:RangeNumberSelector @@ -228,6 +229,8 @@ id: SalvageEquipmentLegendary table: !type:GroupSelector children: + - id: BlueprintFulton + - id: BlueprintSeismicCharge - id: WeaponCrusherGlaive - id: ClothingOuterHardsuitSalvage - id: OmnizineChemistryBottle diff --git a/Resources/Prototypes/Entities/Objects/Tools/blueprint.yml b/Resources/Prototypes/Entities/Objects/Tools/blueprint.yml new file mode 100644 index 0000000000..ba26baf362 --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Tools/blueprint.yml @@ -0,0 +1,49 @@ +- type: entity + parent: BaseItem + id: BaseBlueprint + name: blueprint + description: A blueprint for some machine. It can be inserted into an autolathe. + abstract: true + components: + - type: Sprite + sprite: Objects/Tools/blueprint.rsi + state: icon + - type: Item + sprite: Objects/Tools/blueprint.rsi + size: Normal + - type: Blueprint + - type: StaticPrice + price: 1000 + - type: Tag + tags: + - BlueprintAutolathe + +- type: entity + parent: BaseBlueprint + id: BlueprintFulton + name: fulton blueprint + description: A blueprint with a schematic of a fulton. It can be inserted into an autolathe. + components: + - type: Blueprint + providedRecipes: + - Fulton + +- type: entity + parent: BaseBlueprint + id: BlueprintSeismicCharge + name: seismic charge blueprint + description: A blueprint with a schematic of a seismic charge. It can be inserted into an autolathe. + components: + - type: Blueprint + providedRecipes: + - SeismicCharge + +- type: entity + parent: BaseBlueprint + id: BlueprintFlare + name: flare blueprint + description: A blueprint with a schematic of a flare. It can be inserted into an autolathe. + components: + - type: Blueprint + providedRecipes: + - Flare diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index bf4d4de783..91ac7b049a 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -89,7 +89,7 @@ id: Autolathe parent: BaseLatheLube name: autolathe - description: It produces basic items using metal and glass. + description: It produces basic items using metal and glass. Has the ability to process blueprints to print new recipes. components: - type: Sprite sprite: Structures/Machines/autolathe.rsi @@ -226,6 +226,18 @@ - RiotShield - SpeedLoaderMagnum - SpeedLoaderMagnumEmpty + - type: BlueprintReceiver + whitelist: + tags: + - BlueprintAutolathe + - type: ContainerContainer + containers: + machine_board: !type:Container + machine_parts: !type:Container + blueprint: !type:Container + - type: EmptyOnMachineDeconstruct + containers: + - blueprint - type: entity id: AutolatheHyperConvection diff --git a/Resources/Prototypes/Recipes/Lathes/salvage.yml b/Resources/Prototypes/Recipes/Lathes/salvage.yml index 84047ae75d..2def767e91 100644 --- a/Resources/Prototypes/Recipes/Lathes/salvage.yml +++ b/Resources/Prototypes/Recipes/Lathes/salvage.yml @@ -1,20 +1,32 @@ - type: latheRecipe id: Fulton result: Fulton1 + category: Tools completetime: 1 materials: Steel: 200 - Cloth: 100 + Cloth: 500 - type: latheRecipe id: FultonBeacon result: FultonBeacon + category: Tools completetime: 5 materials: Steel: 1000 Glass: 500 # If they get spammed make it cost silver. +- type: latheRecipe + id: SeismicCharge + result: SeismicCharge + category: Tools + completetime: 1 + materials: + Plastic: 1500 + Steel: 100 + Silver: 100 + - type: latheRecipe id: MiningDrill result: MiningDrill diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index 9b11570a25..b52e2c4e8c 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -60,6 +60,9 @@ - type: Tag id: Bloodpack +- type: Tag + id: BlueprintAutolathe + - type: Tag id: BodyBag diff --git a/Resources/Textures/Objects/Tools/blueprint.rsi/icon.png b/Resources/Textures/Objects/Tools/blueprint.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..fe8b37fa2534c7753ee8f254ce486466886bacee GIT binary patch literal 449 zcmV;y0Y3hTP)Px$dr3q=R9J=WRxMA&KoET;{D+E$P+3!3#EA+*0=A+8g(B$QFX#`TnxIf8NLUL= zgQy@W`pn6aWks<#Oi{bp>j#-F*C6Oin(j{9d2e=Rb_>jyG2@S6nSKzYc{B8^7NTQ& zyS*^x7(~o?6Eohte}1VeNb}A(X1w`Hs1CBanDM4sZ#s6CgaCjjE0oRkJ={kJB;Wf~ z0ez%8F-t;3Spk-WNb;KOYi|s3loeYM_X(RsfFAA8^FCoafh;V&0Q-tDNb@FUoK%3o zh!n^OWwgJI*hoNIOkXNY>%gcDvlCm*7t0l1uFnCWvkH^7p-yBwj%qs{leVEGXpd_l zPzwQ;gjg(BM*fTi=ut;mp$O%Q$h1&llmfjA+FP(bcYTlSb94&ydT85TpPtG7NR=uN zB6-}}j)PhVb(fHHlOve40@Zp0*Y|LAdJKz~YWz_?z~beW-Qp#c8vtPi4lnP>+bUrG r12Xusdv;|+Ts_|G?$2k&jDLl14J=gpqz$|D^@QWdRP{hmYuV%YrD%(Iltw|?|0L! z)BALofu=DqWIs){eUfUMqxwBVRzpYn^~J(OD~3xKu0Kwboc+@0^+AJp#$BJqdap{# zF;{Qm(^+-Q>XPa8RQ?Y&tZR1eVRF4X!|aCgfchzZz7pvbaz0bX5AG^k? zXR}v-UO&CKB<-nF>8WSyjxSG(U!tb?WE<0xpt}<+s~i6HPT@^2UgMm5{r{Rx8>fB< jpZy?W=gv}~XA<=7Omvp4aq--j2eRJN)z4*}Q$iB}L4J)+ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tools/blueprint.rsi/inhand-right.png b/Resources/Textures/Objects/Tools/blueprint.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..37dd59e5c3a6ade73d6c428765d5c01995bda25f GIT binary patch literal 358 zcmV-s0h#`ZP)Px$AW1|)RCt{2+OZLWKoEuD!*R^)z&c2cY`{`%z@Y(4u>m4d>#zed6O49dhv8(G z;W*L%OC0rXc^n)DYX|@U0000WrmX6&tm-azjxpYx8*9<&k@B!+HZ4F1A>1!VSK%Ba zn`E^ZT>yQ;p2E54jFyYFwH7|#nu(E?<<05d0&6hq7LbDHTeFHqNRqXz>h9UTx^Mp} zlzfskN%2{WCa{*A0000000000V0lICi=pPi<9aH_#(dJB?>V+-zQa{W(+mbaT|O7;TPzYBV19yF!nXSKXra_UmK+M@ZG4k1l}q;IX@4Zj0XmLnr-g zU(qYrUod&E>sa=%9wT$5<(-1-At)r|FFXVR008i>-o@yotipFaz5oCK07*qoM6N<$ Ef>{QlGXMYp literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Tools/blueprint.rsi/meta.json b/Resources/Textures/Objects/Tools/blueprint.rsi/meta.json new file mode 100644 index 0000000000..ab9e7fb2a9 --- /dev/null +++ b/Resources/Textures/Objects/Tools/blueprint.rsi/meta.json @@ -0,0 +1,22 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/vgstation-coders/vgstation13/commit/dd749c36c416a6960782732cecf25e5ebac326e8", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "icon" + } + ] +}