diff --git a/Content.Server/GameObjects/Components/Chemistry/RehydratableComponent.cs b/Content.Server/GameObjects/Components/Chemistry/RehydratableComponent.cs new file mode 100644 index 0000000000..f727d00616 --- /dev/null +++ b/Content.Server/GameObjects/Components/Chemistry/RehydratableComponent.cs @@ -0,0 +1,92 @@ +#nullable enable +using System; +using System.Collections.Generic; +using Content.Server.GameObjects.Components.Body.Digestive; +using Content.Server.GameObjects.Components.Chemistry; +using Content.Server.GameObjects.Components.GUI; +using Content.Server.GameObjects.Components.Items.Storage; +using Content.Server.GameObjects.EntitySystems; +using Content.Server.Utility; +using Content.Shared.Chemistry; +using Content.Shared.Interfaces; +using Content.Shared.Interfaces.GameObjects.Components; +using Content.Shared.Utility; +using Robust.Server.GameObjects.EntitySystems; +using Robust.Server.GameObjects; +using Robust.Shared.Audio; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.GameObjects.Components; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Serialization; +using Robust.Shared.ViewVariables; +using Robust.Shared.Log; +using Robust.Shared.Prototypes; +using Robust.Shared.Utility; + +namespace Content.Server.GameObjects.Components.Chemistry +{ + /// + /// Basically, monkey cubes. + /// But specifically, this component deletes the entity and spawns in a new entity when the entity is exposed to a given reagent. + /// + [RegisterComponent] + [ComponentReference(typeof(IReagentReaction))] + [ComponentReference(typeof(ISolutionChange))] + public class RehydratableComponent : Component, IReagentReaction, ISolutionChange + { + public override string Name => "Rehydratable"; + + [ViewVariables] + private string _catalystPrototype = ""; + [ViewVariables] + private string? _targetPrototype; + + private bool _expanding; + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + serializer.DataField(ref _catalystPrototype, "catalyst", "chem.H2O"); + serializer.DataField(ref _targetPrototype, "target", null); + } + + ReagentUnit IReagentReaction.ReagentReactTouch(ReagentPrototype reagent, ReagentUnit volume) => Reaction(reagent, volume); + ReagentUnit IReagentReaction.ReagentReactInjection(ReagentPrototype reagent, ReagentUnit volume) => Reaction(reagent, volume); + + private ReagentUnit Reaction(ReagentPrototype reagent, ReagentUnit volume) + { + if ((volume > ReagentUnit.Zero) && (reagent.ID == _catalystPrototype)) + { + Expand(); + } + return ReagentUnit.Zero; + } + + void ISolutionChange.SolutionChanged(SolutionChangeEventArgs eventArgs) + { + var solution = eventArgs.Owner.GetComponent(); + if (solution.Solution.GetReagentQuantity(_catalystPrototype) > ReagentUnit.Zero) + { + Expand(); + } + } + + // Try not to make this public if you can help it. + private void Expand() + { + if (_expanding) + { + return; + } + _expanding = true; + Owner.PopupMessageEveryone(Loc.GetString("{0:TheName} expands!", Owner)); + if (!string.IsNullOrEmpty(_targetPrototype)) + { + Owner.EntityManager.SpawnEntity(_targetPrototype, Owner.Transform.Coordinates); + } + Owner.Delete(); + } + } +} diff --git a/Content.Server/GameObjects/Components/Kitchen/ButcherableComponent.cs b/Content.Server/GameObjects/Components/Kitchen/ButcherableComponent.cs new file mode 100644 index 0000000000..0e7d7b3dbc --- /dev/null +++ b/Content.Server/GameObjects/Components/Kitchen/ButcherableComponent.cs @@ -0,0 +1,30 @@ +#nullable enable +using System; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization; +using Robust.Shared.ViewVariables; + +namespace Content.Server.GameObjects.Components.Kitchen +{ + /// + /// Indicates that the entity can be thrown on a kitchen spike for butchering. + /// + [RegisterComponent] + public class ButcherableComponent : Component + { + public override string Name => "Butcherable"; + + [ViewVariables] + public string? MeatPrototype => _meatPrototype; + + [ViewVariables] + private string? _meatPrototype; + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + serializer.DataField(ref _meatPrototype, "meat", null); + } + } +} + diff --git a/Content.Server/GameObjects/Components/Kitchen/KitchenSpikeComponent.cs b/Content.Server/GameObjects/Components/Kitchen/KitchenSpikeComponent.cs new file mode 100644 index 0000000000..35bfe18dc8 --- /dev/null +++ b/Content.Server/GameObjects/Components/Kitchen/KitchenSpikeComponent.cs @@ -0,0 +1,114 @@ +#nullable enable +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Content.Server.GameObjects.Components.Body; +using Content.Server.GameObjects.Components.Chemistry; +using Content.Server.GameObjects.Components.GUI; +using Content.Server.GameObjects.Components.Items.Storage; +using Content.Server.GameObjects.Components.Power.ApcNetComponents; +using Content.Server.GameObjects.EntitySystems; +using Content.Server.Interfaces.Chat; +using Content.Server.Interfaces.GameObjects; +using Content.Server.Utility; +using Content.Shared.Chemistry; +using Content.Shared.GameObjects.Components.Body; +using Content.Shared.GameObjects.Components.Power; +using Content.Shared.Interfaces; +using Content.Shared.Interfaces.GameObjects.Components; +using Content.Shared.Kitchen; +using Content.Shared.Prototypes.Kitchen; +using Robust.Server.GameObjects; +using Robust.Server.GameObjects.Components.Container; +using Robust.Server.GameObjects.Components.UserInterface; +using Robust.Server.GameObjects.EntitySystems; +using Robust.Server.Interfaces.GameObjects; +using Robust.Shared.Audio; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Systems; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Serialization; +using Robust.Shared.Timers; +using Robust.Shared.ViewVariables; + +namespace Content.Server.GameObjects.Components.Kitchen +{ + [RegisterComponent] + [ComponentReference(typeof(IActivate))] + public class KitchenSpikeComponent : Component, IActivate, ISuicideAct + { + public override string Name => "KitchenSpike"; + + private int _meatParts; + private string? _meatPrototype; + private string _meatSource1p = "?"; + private string _meatSource0 = "?"; + + void IActivate.Activate(ActivateEventArgs eventArgs) + { + var victim = eventArgs.User.GetComponent().PulledObject?.Owner; + + var sprite = Owner.GetComponent(); + + if (victim == null) + { + if (_meatParts == 0) + { + return; + } + _meatParts--; + + if (!string.IsNullOrEmpty(_meatPrototype)) + { + Owner.EntityManager.SpawnEntity(_meatPrototype, Owner.Transform.Coordinates); + } + + if (_meatParts != 0) + { + eventArgs.User.PopupMessage(_meatSource1p); + } + else + { + sprite.LayerSetState(0, "spike"); + eventArgs.User.PopupMessage(_meatSource0); + } + return; + } + else if (_meatParts > 0) + { + Owner.PopupMessage(eventArgs.User, Loc.GetString("The spike already has something on it, finish collecting its meat first!")); + return; + } + + if (!victim.TryGetComponent(out var food)) + { + Owner.PopupMessage(eventArgs.User, Loc.GetString("{0:theName} can't be butchered on the spike.", victim)); + return; + } + + _meatPrototype = food.MeatPrototype; + _meatParts = 5; + _meatSource1p = Loc.GetString("You remove some meat from {0:theName}.", victim); + _meatSource0 = Loc.GetString("You remove the last piece of meat from {0:theName}!", victim); + + sprite.LayerSetState(0, "spikebloody"); + + Owner.PopupMessageEveryone(Loc.GetString("{0:theName} has forced {1:theName} onto the spike, killing them instantly!", eventArgs.User, victim)); + victim.Delete(); + } + + public SuicideKind Suicide(IEntity victim, IChatManager chat) + { + var othersMessage = Loc.GetString("{0:theName} has thrown themselves on a meat spike!", victim); + victim.PopupMessageOtherClients(othersMessage); + + var selfMessage = Loc.GetString("You throw yourself on a meat spike!"); + victim.PopupMessage(selfMessage); + + return SuicideKind.Piercing; + } + } +} diff --git a/Content.Shared/GameObjects/Components/Items/SharedHandsComponent.cs b/Content.Shared/GameObjects/Components/Items/SharedHandsComponent.cs index 02919bcf8e..9e16860294 100644 --- a/Content.Shared/GameObjects/Components/Items/SharedHandsComponent.cs +++ b/Content.Shared/GameObjects/Components/Items/SharedHandsComponent.cs @@ -15,7 +15,7 @@ namespace Content.Shared.GameObjects.Components.Items public sealed override uint? NetID => ContentNetIDs.HANDS; [ViewVariables] - protected ICollidableComponent? PulledObject; + public ICollidableComponent? PulledObject { get; protected set; } [ViewVariables] protected bool IsPulling => PulledObject != null; diff --git a/Resources/Prototypes/Entities/Constructible/Ground/kitchen.yml b/Resources/Prototypes/Entities/Constructible/Ground/kitchen.yml new file mode 100644 index 0000000000..ab76b8d1c2 --- /dev/null +++ b/Resources/Prototypes/Entities/Constructible/Ground/kitchen.yml @@ -0,0 +1,23 @@ +- type: entity + id: KitchenSpike + name: meat spike + description: A spike for collecting meat from animals. + components: + - type: Clickable + - type: InteractionOutline + - type: Collidable + shapes: + - !type:PhysShapeAabb + mask: + - Impassable + layer: + - Impassable + - type: Sprite + # temp to make clickmask work + sprite: Constructible/Misc/kitchen.rsi + state: spike + - type: Anchorable + - type: Pullable + - type: Destructible + deadThreshold: 50 + - type: KitchenSpike diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index a4dd7a47b5..317de00e00 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -60,6 +60,8 @@ normal: monkey dead: dead - type: Pullable + - type: Butcherable + meat: FoodMeat - type: entity save: false diff --git a/Resources/Prototypes/Entities/Objects/Consumable/food.yml b/Resources/Prototypes/Entities/Objects/Consumable/food.yml index a22396e796..9c35a42c1b 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/food.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/food.yml @@ -1705,9 +1705,12 @@ reagents: - ReagentId: chem.Nutriment Quantity: 10 + maxVol: 11 # needs room for water + caps: AddTo, RemoveFrom, FitsInDispenser - type: Sprite sprite: Objects/Consumable/Food/monkeycube.rsi - + - type: Rehydratable + target: MonkeyMob_Content - type: entity parent: FoodBase diff --git a/Resources/Prototypes/Recipes/Construction/kitchen.yml b/Resources/Prototypes/Recipes/Construction/kitchen.yml new file mode 100644 index 0000000000..75a4504c10 --- /dev/null +++ b/Resources/Prototypes/Recipes/Construction/kitchen.yml @@ -0,0 +1,18 @@ +- type: construction + name: meat spike + id: KitchenSpike + category: Items/Kitchen + keywords: [kitchen] + description: + icon: Constructible/Misc/kitchen.rsi/spike.png + result: KitchenSpike + objectType: Structure + steps: + # Replace with Metal Rods whenever + # also this should be done with a Welding tool if that is specifiable + - material: Metal + amount: 3 + reverse: + # logic here: BOLT_TURNING -> Wrench -> Anchoring + tool: Anchoring + diff --git a/Resources/Textures/Constructible/Misc/kitchen.rsi/meta.json b/Resources/Textures/Constructible/Misc/kitchen.rsi/meta.json new file mode 100644 index 0000000000..7bf3d1be3c --- /dev/null +++ b/Resources/Textures/Constructible/Misc/kitchen.rsi/meta.json @@ -0,0 +1,12 @@ +{ + "version":1, + "size":{"x":32,"y":32}, + "license":"CC-BY-SA-3.0", + "copyright":"Taken from https://github.com/discordia-space/CEV-Eris/blob/2b969adc2dfd3e9621bf3597c5cbffeb3ac8c9f0/icons/obj/kitchen.dmi", + "states":[ + {"name":"spike","directions":1,"delays":[[1.0]]}, + {"name":"spikebloody","directions":1,"delays":[[1.0]]}, + {"name":"spikebloodygreen","directions":1,"delays":[[1.0]]} + ] +} + diff --git a/Resources/Textures/Constructible/Misc/kitchen.rsi/spike.png b/Resources/Textures/Constructible/Misc/kitchen.rsi/spike.png new file mode 100644 index 0000000000..f39fafcf20 Binary files /dev/null and b/Resources/Textures/Constructible/Misc/kitchen.rsi/spike.png differ diff --git a/Resources/Textures/Constructible/Misc/kitchen.rsi/spikebloody.png b/Resources/Textures/Constructible/Misc/kitchen.rsi/spikebloody.png new file mode 100644 index 0000000000..e914c0225c Binary files /dev/null and b/Resources/Textures/Constructible/Misc/kitchen.rsi/spikebloody.png differ diff --git a/Resources/Textures/Constructible/Misc/kitchen.rsi/spikebloodygreen.png b/Resources/Textures/Constructible/Misc/kitchen.rsi/spikebloodygreen.png new file mode 100644 index 0000000000..213c7247ba Binary files /dev/null and b/Resources/Textures/Constructible/Misc/kitchen.rsi/spikebloodygreen.png differ